roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fs
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     fa: '',
653     badge: '',
654     theme: 'default',
655     inverse: false,
656     
657     toggle: false,
658     ontext: 'ON',
659     offtext: 'OFF',
660     defaulton: true,
661     preventDefault: true,
662     removeClass: false,
663     name: false,
664     target: false,
665      
666     pressed : null,
667      
668     
669     getAutoCreate : function(){
670         
671         var cfg = {
672             tag : 'button',
673             cls : 'roo-button',
674             html: ''
675         };
676         
677         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
679             this.tag = 'button';
680         } else {
681             cfg.tag = this.tag;
682         }
683         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684         
685         if (this.toggle == true) {
686             cfg={
687                 tag: 'div',
688                 cls: 'slider-frame roo-button',
689                 cn: [
690                     {
691                         tag: 'span',
692                         'data-on-text':'ON',
693                         'data-off-text':'OFF',
694                         cls: 'slider-button',
695                         html: this.offtext
696                     }
697                 ]
698             };
699             
700             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701                 cfg.cls += ' '+this.weight;
702             }
703             
704             return cfg;
705         }
706         
707         if (this.isClose) {
708             cfg.cls += ' close';
709             
710             cfg["aria-hidden"] = true;
711             
712             cfg.html = "&times;";
713             
714             return cfg;
715         }
716         
717          
718         if (this.theme==='default') {
719             cfg.cls = 'btn roo-button';
720             
721             //if (this.parentType != 'Navbar') {
722             this.weight = this.weight.length ?  this.weight : 'default';
723             //}
724             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725                 
726                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728                 cfg.cls += ' btn-' + outline + weight;
729                 if (this.weight == 'default') {
730                     // BC
731                     cfg.cls += ' btn-' + this.weight;
732                 }
733             }
734         } else if (this.theme==='glow') {
735             
736             cfg.tag = 'a';
737             cfg.cls = 'btn-glow roo-button';
738             
739             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 cfg.cls += ' ' + this.weight;
742             }
743         }
744    
745         
746         if (this.inverse) {
747             this.cls += ' inverse';
748         }
749         
750         
751         if (this.active || this.pressed === true) {
752             cfg.cls += ' active';
753         }
754         
755         if (this.disabled) {
756             cfg.disabled = 'disabled';
757         }
758         
759         if (this.items) {
760             Roo.log('changing to ul' );
761             cfg.tag = 'ul';
762             this.glyphicon = 'caret';
763             if (Roo.bootstrap.version == 4) {
764                 this.fa = 'caret-down';
765             }
766             
767         }
768         
769         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
770          
771         //gsRoo.log(this.parentType);
772         if (this.parentType === 'Navbar' && !this.parent().bar) {
773             Roo.log('changing to li?');
774             
775             cfg.tag = 'li';
776             
777             cfg.cls = '';
778             cfg.cn =  [{
779                 tag : 'a',
780                 cls : 'roo-button',
781                 html : this.html,
782                 href : this.href || '#'
783             }];
784             if (this.menu) {
785                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
786                 cfg.cls += ' dropdown';
787             }   
788             
789             delete cfg.html;
790             
791         }
792         
793        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
794         
795         if (this.glyphicon) {
796             cfg.html = ' ' + cfg.html;
797             
798             cfg.cn = [
799                 {
800                     tag: 'span',
801                     cls: 'glyphicon glyphicon-' + this.glyphicon
802                 }
803             ];
804         }
805         if (this.fa) {
806             cfg.html = ' ' + cfg.html;
807             
808             cfg.cn = [
809                 {
810                     tag: 'i',
811                     cls: 'fa fas fa-' + this.fa
812                 }
813             ];
814         }
815         
816         if (this.badge) {
817             cfg.html += ' ';
818             
819             cfg.tag = 'a';
820             
821 //            cfg.cls='btn roo-button';
822             
823             cfg.href=this.href;
824             
825             var value = cfg.html;
826             
827             if(this.glyphicon){
828                 value = {
829                     tag: 'span',
830                     cls: 'glyphicon glyphicon-' + this.glyphicon,
831                     html: this.html
832                 };
833             }
834             if(this.fa){
835                 value = {
836                     tag: 'i',
837                     cls: 'fa fas fa-' + this.fa,
838                     html: this.html
839                 };
840             }
841             
842             var bw = this.badge_weight.length ? this.badge_weight :
843                 (this.weight.length ? this.weight : 'secondary');
844             bw = bw == 'default' ? 'secondary' : bw;
845             
846             cfg.cn = [
847                 value,
848                 {
849                     tag: 'span',
850                     cls: 'badge badge-' + bw,
851                     html: this.badge
852                 }
853             ];
854             
855             cfg.html='';
856         }
857         
858         if (this.menu) {
859             cfg.cls += ' dropdown';
860             cfg.html = typeof(cfg.html) != 'undefined' ?
861                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
862         }
863         
864         if (cfg.tag !== 'a' && this.href !== '') {
865             throw "Tag must be a to set href.";
866         } else if (this.href.length > 0) {
867             cfg.href = this.href;
868         }
869         
870         if(this.removeClass){
871             cfg.cls = '';
872         }
873         
874         if(this.target){
875             cfg.target = this.target;
876         }
877         
878         return cfg;
879     },
880     initEvents: function() {
881        // Roo.log('init events?');
882 //        Roo.log(this.el.dom);
883         // add the menu...
884         
885         if (typeof (this.menu) != 'undefined') {
886             this.menu.parentType = this.xtype;
887             this.menu.triggerEl = this.el;
888             this.addxtype(Roo.apply({}, this.menu));
889         }
890
891
892        if (this.el.hasClass('roo-button')) {
893             this.el.on('click', this.onClick, this);
894        } else {
895             this.el.select('.roo-button').on('click', this.onClick, this);
896        }
897        
898        if(this.removeClass){
899            this.el.on('click', this.onClick, this);
900        }
901        
902        this.el.enableDisplayMode();
903         
904     },
905     onClick : function(e)
906     {
907         if (this.disabled) {
908             return;
909         }
910         
911         Roo.log('button on click ');
912         if(this.preventDefault){
913             e.preventDefault();
914         }
915         
916         if (this.pressed === true || this.pressed === false) {
917             this.toggleActive(e);
918         }
919         
920         
921         this.fireEvent('click', this, e);
922     },
923     
924     /**
925      * Enables this button
926      */
927     enable : function()
928     {
929         this.disabled = false;
930         this.el.removeClass('disabled');
931     },
932     
933     /**
934      * Disable this button
935      */
936     disable : function()
937     {
938         this.disabled = true;
939         this.el.addClass('disabled');
940     },
941      /**
942      * sets the active state on/off, 
943      * @param {Boolean} state (optional) Force a particular state
944      */
945     setActive : function(v) {
946         
947         this.el[v ? 'addClass' : 'removeClass']('active');
948         this.pressed = v;
949     },
950      /**
951      * toggles the current active state 
952      */
953     toggleActive : function(e)
954     {
955         this.setActive(!this.pressed);
956         this.fireEvent('toggle', this, e, !this.pressed);
957     },
958      /**
959      * get the current active state
960      * @return {boolean} true if it's active
961      */
962     isActive : function()
963     {
964         return this.el.hasClass('active');
965     },
966     /**
967      * set the text of the first selected button
968      */
969     setText : function(str)
970     {
971         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
972     },
973     /**
974      * get the text of the first selected button
975      */
976     getText : function()
977     {
978         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
979     },
980     
981     setWeight : function(str)
982     {
983         this.el.removeClass(this.weightClass);
984         this.weight = str;
985         var outline = this.outline ? 'outline-' : '';
986         if (str == 'default') {
987             this.el.addClass('btn-default btn-outline-secondary');        
988             return;
989         }
990         this.el.addClass('btn-' + outline + str);        
991     }
992     
993     
994 });
995
996  /*
997  * - LGPL
998  *
999  * column
1000  * 
1001  */
1002
1003 /**
1004  * @class Roo.bootstrap.Column
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Column class
1007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1015  *
1016  * 
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019  * @cfg {String} fa (ban|check|...) font awesome icon
1020  * @cfg {Number} fasize (1|2|....) font awsome size
1021
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023
1024  * @cfg {String} html content of column.
1025  * 
1026  * @constructor
1027  * Create a new Column
1028  * @param {Object} config The config object
1029  */
1030
1031 Roo.bootstrap.Column = function(config){
1032     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1033 };
1034
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1036     
1037     xs: false,
1038     sm: false,
1039     md: false,
1040     lg: false,
1041     xsoff: false,
1042     smoff: false,
1043     mdoff: false,
1044     lgoff: false,
1045     html: '',
1046     offset: 0,
1047     alert: false,
1048     fa: false,
1049     icon : false,
1050     hidden : false,
1051     fasize : 1,
1052     
1053     getAutoCreate : function(){
1054         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1055         
1056         cfg = {
1057             tag: 'div',
1058             cls: 'column'
1059         };
1060         
1061         var settings=this;
1062         ['xs','sm','md','lg'].map(function(size){
1063             //Roo.log( size + ':' + settings[size]);
1064             
1065             if (settings[size+'off'] !== false) {
1066                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1067             }
1068             
1069             if (settings[size] === false) {
1070                 return;
1071             }
1072             
1073             if (!settings[size]) { // 0 = hidden
1074                 cfg.cls += ' hidden-' + size;
1075                 return;
1076             }
1077             cfg.cls += ' col-' + size + '-' + settings[size];
1078             
1079         });
1080         
1081         if (this.hidden) {
1082             cfg.cls += ' hidden';
1083         }
1084         
1085         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086             cfg.cls +=' alert alert-' + this.alert;
1087         }
1088         
1089         
1090         if (this.html.length) {
1091             cfg.html = this.html;
1092         }
1093         if (this.fa) {
1094             var fasize = '';
1095             if (this.fasize > 1) {
1096                 fasize = ' fa-' + this.fasize + 'x';
1097             }
1098             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1099             
1100             
1101         }
1102         if (this.icon) {
1103             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1104         }
1105         
1106         return cfg;
1107     }
1108    
1109 });
1110
1111  
1112
1113  /*
1114  * - LGPL
1115  *
1116  * page container.
1117  * 
1118  */
1119
1120
1121 /**
1122  * @class Roo.bootstrap.Container
1123  * @extends Roo.bootstrap.Component
1124  * Bootstrap Container class
1125  * @cfg {Boolean} jumbotron is it a jumbotron element
1126  * @cfg {String} html content of element
1127  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1129  * @cfg {String} header content of header (for panel)
1130  * @cfg {String} footer content of footer (for panel)
1131  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132  * @cfg {String} tag (header|aside|section) type of HTML tag.
1133  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134  * @cfg {String} fa font awesome icon
1135  * @cfg {String} icon (info-sign|check|...) glyphicon name
1136  * @cfg {Boolean} hidden (true|false) hide the element
1137  * @cfg {Boolean} expandable (true|false) default false
1138  * @cfg {Boolean} expanded (true|false) default true
1139  * @cfg {String} rheader contet on the right of header
1140  * @cfg {Boolean} clickable (true|false) default false
1141
1142  *     
1143  * @constructor
1144  * Create a new Container
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Container = function(config){
1149     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // raw events
1153          /**
1154          * @event expand
1155          * After the panel has been expand
1156          * 
1157          * @param {Roo.bootstrap.Container} this
1158          */
1159         "expand" : true,
1160         /**
1161          * @event collapse
1162          * After the panel has been collapsed
1163          * 
1164          * @param {Roo.bootstrap.Container} this
1165          */
1166         "collapse" : true,
1167         /**
1168          * @event click
1169          * When a element is chick
1170          * @param {Roo.bootstrap.Container} this
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1178     
1179     jumbotron : false,
1180     well: '',
1181     panel : '',
1182     header: '',
1183     footer : '',
1184     sticky: '',
1185     tag : false,
1186     alert : false,
1187     fa: false,
1188     icon : false,
1189     expandable : false,
1190     rheader : '',
1191     expanded : true,
1192     clickable: false,
1193   
1194      
1195     getChildContainer : function() {
1196         
1197         if(!this.el){
1198             return false;
1199         }
1200         
1201         if (this.panel.length) {
1202             return this.el.select('.panel-body',true).first();
1203         }
1204         
1205         return this.el;
1206     },
1207     
1208     
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag : this.tag || 'div',
1213             html : '',
1214             cls : ''
1215         };
1216         if (this.jumbotron) {
1217             cfg.cls = 'jumbotron';
1218         }
1219         
1220         
1221         
1222         // - this is applied by the parent..
1223         //if (this.cls) {
1224         //    cfg.cls = this.cls + '';
1225         //}
1226         
1227         if (this.sticky.length) {
1228             
1229             var bd = Roo.get(document.body);
1230             if (!bd.hasClass('bootstrap-sticky')) {
1231                 bd.addClass('bootstrap-sticky');
1232                 Roo.select('html',true).setStyle('height', '100%');
1233             }
1234              
1235             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1236         }
1237         
1238         
1239         if (this.well.length) {
1240             switch (this.well) {
1241                 case 'lg':
1242                 case 'sm':
1243                     cfg.cls +=' well well-' +this.well;
1244                     break;
1245                 default:
1246                     cfg.cls +=' well';
1247                     break;
1248             }
1249         }
1250         
1251         if (this.hidden) {
1252             cfg.cls += ' hidden';
1253         }
1254         
1255         
1256         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257             cfg.cls +=' alert alert-' + this.alert;
1258         }
1259         
1260         var body = cfg;
1261         
1262         if (this.panel.length) {
1263             cfg.cls += ' panel panel-' + this.panel;
1264             cfg.cn = [];
1265             if (this.header.length) {
1266                 
1267                 var h = [];
1268                 
1269                 if(this.expandable){
1270                     
1271                     cfg.cls = cfg.cls + ' expandable';
1272                     
1273                     h.push({
1274                         tag: 'i',
1275                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1276                     });
1277                     
1278                 }
1279                 
1280                 h.push(
1281                     {
1282                         tag: 'span',
1283                         cls : 'panel-title',
1284                         html : (this.expandable ? '&nbsp;' : '') + this.header
1285                     },
1286                     {
1287                         tag: 'span',
1288                         cls: 'panel-header-right',
1289                         html: this.rheader
1290                     }
1291                 );
1292                 
1293                 cfg.cn.push({
1294                     cls : 'panel-heading',
1295                     style : this.expandable ? 'cursor: pointer' : '',
1296                     cn : h
1297                 });
1298                 
1299             }
1300             
1301             body = false;
1302             cfg.cn.push({
1303                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1304                 html : this.html
1305             });
1306             
1307             
1308             if (this.footer.length) {
1309                 cfg.cn.push({
1310                     cls : 'panel-footer',
1311                     html : this.footer
1312                     
1313                 });
1314             }
1315             
1316         }
1317         
1318         if (body) {
1319             body.html = this.html || cfg.html;
1320             // prefix with the icons..
1321             if (this.fa) {
1322                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1323             }
1324             if (this.icon) {
1325                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1326             }
1327             
1328             
1329         }
1330         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331             cfg.cls =  'container';
1332         }
1333         
1334         return cfg;
1335     },
1336     
1337     initEvents: function() 
1338     {
1339         if(this.expandable){
1340             var headerEl = this.headerEl();
1341         
1342             if(headerEl){
1343                 headerEl.on('click', this.onToggleClick, this);
1344             }
1345         }
1346         
1347         if(this.clickable){
1348             this.el.on('click', this.onClick, this);
1349         }
1350         
1351     },
1352     
1353     onToggleClick : function()
1354     {
1355         var headerEl = this.headerEl();
1356         
1357         if(!headerEl){
1358             return;
1359         }
1360         
1361         if(this.expanded){
1362             this.collapse();
1363             return;
1364         }
1365         
1366         this.expand();
1367     },
1368     
1369     expand : function()
1370     {
1371         if(this.fireEvent('expand', this)) {
1372             
1373             this.expanded = true;
1374             
1375             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1376             
1377             this.el.select('.panel-body',true).first().removeClass('hide');
1378             
1379             var toggleEl = this.toggleEl();
1380
1381             if(!toggleEl){
1382                 return;
1383             }
1384
1385             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1386         }
1387         
1388     },
1389     
1390     collapse : function()
1391     {
1392         if(this.fireEvent('collapse', this)) {
1393             
1394             this.expanded = false;
1395             
1396             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397             this.el.select('.panel-body',true).first().addClass('hide');
1398         
1399             var toggleEl = this.toggleEl();
1400
1401             if(!toggleEl){
1402                 return;
1403             }
1404
1405             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1406         }
1407     },
1408     
1409     toggleEl : function()
1410     {
1411         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1412             return;
1413         }
1414         
1415         return this.el.select('.panel-heading .fa',true).first();
1416     },
1417     
1418     headerEl : function()
1419     {
1420         if(!this.el || !this.panel.length || !this.header.length){
1421             return;
1422         }
1423         
1424         return this.el.select('.panel-heading',true).first()
1425     },
1426     
1427     bodyEl : function()
1428     {
1429         if(!this.el || !this.panel.length){
1430             return;
1431         }
1432         
1433         return this.el.select('.panel-body',true).first()
1434     },
1435     
1436     titleEl : function()
1437     {
1438         if(!this.el || !this.panel.length || !this.header.length){
1439             return;
1440         }
1441         
1442         return this.el.select('.panel-title',true).first();
1443     },
1444     
1445     setTitle : function(v)
1446     {
1447         var titleEl = this.titleEl();
1448         
1449         if(!titleEl){
1450             return;
1451         }
1452         
1453         titleEl.dom.innerHTML = v;
1454     },
1455     
1456     getTitle : function()
1457     {
1458         
1459         var titleEl = this.titleEl();
1460         
1461         if(!titleEl){
1462             return '';
1463         }
1464         
1465         return titleEl.dom.innerHTML;
1466     },
1467     
1468     setRightTitle : function(v)
1469     {
1470         var t = this.el.select('.panel-header-right',true).first();
1471         
1472         if(!t){
1473             return;
1474         }
1475         
1476         t.dom.innerHTML = v;
1477     },
1478     
1479     onClick : function(e)
1480     {
1481         e.preventDefault();
1482         
1483         this.fireEvent('click', this, e);
1484     }
1485 });
1486
1487  /*
1488  * - LGPL
1489  *
1490  * image
1491  * 
1492  */
1493
1494
1495 /**
1496  * @class Roo.bootstrap.Img
1497  * @extends Roo.bootstrap.Component
1498  * Bootstrap Img class
1499  * @cfg {Boolean} imgResponsive false | true
1500  * @cfg {String} border rounded | circle | thumbnail
1501  * @cfg {String} src image source
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505  * @cfg {String} xsUrl xs image source
1506  * @cfg {String} smUrl sm image source
1507  * @cfg {String} mdUrl md image source
1508  * @cfg {String} lgUrl lg image source
1509  * 
1510  * @constructor
1511  * Create a new Input
1512  * @param {Object} config The config object
1513  */
1514
1515 Roo.bootstrap.Img = function(config){
1516     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1517     
1518     this.addEvents({
1519         // img events
1520         /**
1521          * @event click
1522          * The img click event for the img.
1523          * @param {Roo.EventObject} e
1524          */
1525         "click" : true
1526     });
1527 };
1528
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1530     
1531     imgResponsive: true,
1532     border: '',
1533     src: 'about:blank',
1534     href: false,
1535     target: false,
1536     xsUrl: '',
1537     smUrl: '',
1538     mdUrl: '',
1539     lgUrl: '',
1540
1541     getAutoCreate : function()
1542     {   
1543         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544             return this.createSingleImg();
1545         }
1546         
1547         var cfg = {
1548             tag: 'div',
1549             cls: 'roo-image-responsive-group',
1550             cn: []
1551         };
1552         var _this = this;
1553         
1554         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1555             
1556             if(!_this[size + 'Url']){
1557                 return;
1558             }
1559             
1560             var img = {
1561                 tag: 'img',
1562                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563                 html: _this.html || cfg.html,
1564                 src: _this[size + 'Url']
1565             };
1566             
1567             img.cls += ' roo-image-responsive-' + size;
1568             
1569             var s = ['xs', 'sm', 'md', 'lg'];
1570             
1571             s.splice(s.indexOf(size), 1);
1572             
1573             Roo.each(s, function(ss){
1574                 img.cls += ' hidden-' + ss;
1575             });
1576             
1577             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578                 cfg.cls += ' img-' + _this.border;
1579             }
1580             
1581             if(_this.alt){
1582                 cfg.alt = _this.alt;
1583             }
1584             
1585             if(_this.href){
1586                 var a = {
1587                     tag: 'a',
1588                     href: _this.href,
1589                     cn: [
1590                         img
1591                     ]
1592                 };
1593
1594                 if(this.target){
1595                     a.target = _this.target;
1596                 }
1597             }
1598             
1599             cfg.cn.push((_this.href) ? a : img);
1600             
1601         });
1602         
1603         return cfg;
1604     },
1605     
1606     createSingleImg : function()
1607     {
1608         var cfg = {
1609             tag: 'img',
1610             cls: (this.imgResponsive) ? 'img-responsive' : '',
1611             html : null,
1612             src : 'about:blank'  // just incase src get's set to undefined?!?
1613         };
1614         
1615         cfg.html = this.html || cfg.html;
1616         
1617         cfg.src = this.src || cfg.src;
1618         
1619         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620             cfg.cls += ' img-' + this.border;
1621         }
1622         
1623         if(this.alt){
1624             cfg.alt = this.alt;
1625         }
1626         
1627         if(this.href){
1628             var a = {
1629                 tag: 'a',
1630                 href: this.href,
1631                 cn: [
1632                     cfg
1633                 ]
1634             };
1635             
1636             if(this.target){
1637                 a.target = this.target;
1638             }
1639             
1640         }
1641         
1642         return (this.href) ? a : cfg;
1643     },
1644     
1645     initEvents: function() 
1646     {
1647         if(!this.href){
1648             this.el.on('click', this.onClick, this);
1649         }
1650         
1651     },
1652     
1653     onClick : function(e)
1654     {
1655         Roo.log('img onclick');
1656         this.fireEvent('click', this, e);
1657     },
1658     /**
1659      * Sets the url of the image - used to update it
1660      * @param {String} url the url of the image
1661      */
1662     
1663     setSrc : function(url)
1664     {
1665         this.src =  url;
1666         
1667         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668             this.el.dom.src =  url;
1669             return;
1670         }
1671         
1672         this.el.select('img', true).first().dom.src =  url;
1673     }
1674     
1675     
1676    
1677 });
1678
1679  /*
1680  * - LGPL
1681  *
1682  * image
1683  * 
1684  */
1685
1686
1687 /**
1688  * @class Roo.bootstrap.Link
1689  * @extends Roo.bootstrap.Component
1690  * Bootstrap Link Class
1691  * @cfg {String} alt image alternative text
1692  * @cfg {String} href a tag href
1693  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694  * @cfg {String} html the content of the link.
1695  * @cfg {String} anchor name for the anchor link
1696  * @cfg {String} fa - favicon
1697
1698  * @cfg {Boolean} preventDefault (true | false) default false
1699
1700  * 
1701  * @constructor
1702  * Create a new Input
1703  * @param {Object} config The config object
1704  */
1705
1706 Roo.bootstrap.Link = function(config){
1707     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1708     
1709     this.addEvents({
1710         // img events
1711         /**
1712          * @event click
1713          * The img click event for the img.
1714          * @param {Roo.EventObject} e
1715          */
1716         "click" : true
1717     });
1718 };
1719
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1721     
1722     href: false,
1723     target: false,
1724     preventDefault: false,
1725     anchor : false,
1726     alt : false,
1727     fa: false,
1728
1729
1730     getAutoCreate : function()
1731     {
1732         var html = this.html || '';
1733         
1734         if (this.fa !== false) {
1735             html = '<i class="fa fa-' + this.fa + '"></i>';
1736         }
1737         var cfg = {
1738             tag: 'a'
1739         };
1740         // anchor's do not require html/href...
1741         if (this.anchor === false) {
1742             cfg.html = html;
1743             cfg.href = this.href || '#';
1744         } else {
1745             cfg.name = this.anchor;
1746             if (this.html !== false || this.fa !== false) {
1747                 cfg.html = html;
1748             }
1749             if (this.href !== false) {
1750                 cfg.href = this.href;
1751             }
1752         }
1753         
1754         if(this.alt !== false){
1755             cfg.alt = this.alt;
1756         }
1757         
1758         
1759         if(this.target !== false) {
1760             cfg.target = this.target;
1761         }
1762         
1763         return cfg;
1764     },
1765     
1766     initEvents: function() {
1767         
1768         if(!this.href || this.preventDefault){
1769             this.el.on('click', this.onClick, this);
1770         }
1771     },
1772     
1773     onClick : function(e)
1774     {
1775         if(this.preventDefault){
1776             e.preventDefault();
1777         }
1778         //Roo.log('img onclick');
1779         this.fireEvent('click', this, e);
1780     }
1781    
1782 });
1783
1784  /*
1785  * - LGPL
1786  *
1787  * header
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Header
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Header class
1795  * @cfg {String} html content of header
1796  * @cfg {Number} level (1|2|3|4|5|6) default 1
1797  * 
1798  * @constructor
1799  * Create a new Header
1800  * @param {Object} config The config object
1801  */
1802
1803
1804 Roo.bootstrap.Header  = function(config){
1805     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1806 };
1807
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1809     
1810     //href : false,
1811     html : false,
1812     level : 1,
1813     
1814     
1815     
1816     getAutoCreate : function(){
1817         
1818         
1819         
1820         var cfg = {
1821             tag: 'h' + (1 *this.level),
1822             html: this.html || ''
1823         } ;
1824         
1825         return cfg;
1826     }
1827    
1828 });
1829
1830  
1831
1832  /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842  
1843 /**
1844  * @class Roo.bootstrap.MenuMgr
1845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1846  * @singleton
1847  */
1848 Roo.bootstrap.MenuMgr = function(){
1849    var menus, active, groups = {}, attached = false, lastShow = new Date();
1850
1851    // private - called when first menu is created
1852    function init(){
1853        menus = {};
1854        active = new Roo.util.MixedCollection();
1855        Roo.get(document).addKeyListener(27, function(){
1856            if(active.length > 0){
1857                hideAll();
1858            }
1859        });
1860    }
1861
1862    // private
1863    function hideAll(){
1864        if(active && active.length > 0){
1865            var c = active.clone();
1866            c.each(function(m){
1867                m.hide();
1868            });
1869        }
1870    }
1871
1872    // private
1873    function onHide(m){
1874        active.remove(m);
1875        if(active.length < 1){
1876            Roo.get(document).un("mouseup", onMouseDown);
1877             
1878            attached = false;
1879        }
1880    }
1881
1882    // private
1883    function onShow(m){
1884        var last = active.last();
1885        lastShow = new Date();
1886        active.add(m);
1887        if(!attached){
1888           Roo.get(document).on("mouseup", onMouseDown);
1889            
1890            attached = true;
1891        }
1892        if(m.parentMenu){
1893           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894           m.parentMenu.activeChild = m;
1895        }else if(last && last.isVisible()){
1896           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1897        }
1898    }
1899
1900    // private
1901    function onBeforeHide(m){
1902        if(m.activeChild){
1903            m.activeChild.hide();
1904        }
1905        if(m.autoHideTimer){
1906            clearTimeout(m.autoHideTimer);
1907            delete m.autoHideTimer;
1908        }
1909    }
1910
1911    // private
1912    function onBeforeShow(m){
1913        var pm = m.parentMenu;
1914        if(!pm && !m.allowOtherMenus){
1915            hideAll();
1916        }else if(pm && pm.activeChild && active != m){
1917            pm.activeChild.hide();
1918        }
1919    }
1920
1921    // private this should really trigger on mouseup..
1922    function onMouseDown(e){
1923         Roo.log("on Mouse Up");
1924         
1925         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926             Roo.log("MenuManager hideAll");
1927             hideAll();
1928             e.stopEvent();
1929         }
1930         
1931         
1932    }
1933
1934    // private
1935    function onBeforeCheck(mi, state){
1936        if(state){
1937            var g = groups[mi.group];
1938            for(var i = 0, l = g.length; i < l; i++){
1939                if(g[i] != mi){
1940                    g[i].setChecked(false);
1941                }
1942            }
1943        }
1944    }
1945
1946    return {
1947
1948        /**
1949         * Hides all menus that are currently visible
1950         */
1951        hideAll : function(){
1952             hideAll();  
1953        },
1954
1955        // private
1956        register : function(menu){
1957            if(!menus){
1958                init();
1959            }
1960            menus[menu.id] = menu;
1961            menu.on("beforehide", onBeforeHide);
1962            menu.on("hide", onHide);
1963            menu.on("beforeshow", onBeforeShow);
1964            menu.on("show", onShow);
1965            var g = menu.group;
1966            if(g && menu.events["checkchange"]){
1967                if(!groups[g]){
1968                    groups[g] = [];
1969                }
1970                groups[g].push(menu);
1971                menu.on("checkchange", onCheck);
1972            }
1973        },
1974
1975         /**
1976          * Returns a {@link Roo.menu.Menu} object
1977          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978          * be used to generate and return a new Menu instance.
1979          */
1980        get : function(menu){
1981            if(typeof menu == "string"){ // menu id
1982                return menus[menu];
1983            }else if(menu.events){  // menu instance
1984                return menu;
1985            }
1986            /*else if(typeof menu.length == 'number'){ // array of menu items?
1987                return new Roo.bootstrap.Menu({items:menu});
1988            }else{ // otherwise, must be a config
1989                return new Roo.bootstrap.Menu(menu);
1990            }
1991            */
1992            return false;
1993        },
1994
1995        // private
1996        unregister : function(menu){
1997            delete menus[menu.id];
1998            menu.un("beforehide", onBeforeHide);
1999            menu.un("hide", onHide);
2000            menu.un("beforeshow", onBeforeShow);
2001            menu.un("show", onShow);
2002            var g = menu.group;
2003            if(g && menu.events["checkchange"]){
2004                groups[g].remove(menu);
2005                menu.un("checkchange", onCheck);
2006            }
2007        },
2008
2009        // private
2010        registerCheckable : function(menuItem){
2011            var g = menuItem.group;
2012            if(g){
2013                if(!groups[g]){
2014                    groups[g] = [];
2015                }
2016                groups[g].push(menuItem);
2017                menuItem.on("beforecheckchange", onBeforeCheck);
2018            }
2019        },
2020
2021        // private
2022        unregisterCheckable : function(menuItem){
2023            var g = menuItem.group;
2024            if(g){
2025                groups[g].remove(menuItem);
2026                menuItem.un("beforecheckchange", onBeforeCheck);
2027            }
2028        }
2029    };
2030 }();/*
2031  * - LGPL
2032  *
2033  * menu
2034  * 
2035  */
2036
2037 /**
2038  * @class Roo.bootstrap.Menu
2039  * @extends Roo.bootstrap.Component
2040  * Bootstrap Menu class - container for MenuItems
2041  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2043  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2044  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2045  * 
2046  * @constructor
2047  * Create a new Menu
2048  * @param {Object} config The config object
2049  */
2050
2051
2052 Roo.bootstrap.Menu = function(config){
2053     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054     if (this.registerMenu && this.type != 'treeview')  {
2055         Roo.bootstrap.MenuMgr.register(this);
2056     }
2057     
2058     
2059     this.addEvents({
2060         /**
2061          * @event beforeshow
2062          * Fires before this menu is displayed
2063          * @param {Roo.menu.Menu} this
2064          */
2065         beforeshow : true,
2066         /**
2067          * @event beforehide
2068          * Fires before this menu is hidden
2069          * @param {Roo.menu.Menu} this
2070          */
2071         beforehide : true,
2072         /**
2073          * @event show
2074          * Fires after this menu is displayed
2075          * @param {Roo.menu.Menu} this
2076          */
2077         show : true,
2078         /**
2079          * @event hide
2080          * Fires after this menu is hidden
2081          * @param {Roo.menu.Menu} this
2082          */
2083         hide : true,
2084         /**
2085          * @event click
2086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087          * @param {Roo.menu.Menu} this
2088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089          * @param {Roo.EventObject} e
2090          */
2091         click : true,
2092         /**
2093          * @event mouseover
2094          * Fires when the mouse is hovering over this menu
2095          * @param {Roo.menu.Menu} this
2096          * @param {Roo.EventObject} e
2097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2098          */
2099         mouseover : true,
2100         /**
2101          * @event mouseout
2102          * Fires when the mouse exits this menu
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.EventObject} e
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          */
2107         mouseout : true,
2108         /**
2109          * @event itemclick
2110          * Fires when a menu item contained in this menu is clicked
2111          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112          * @param {Roo.EventObject} e
2113          */
2114         itemclick: true
2115     });
2116     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2120     
2121    /// html : false,
2122     //align : '',
2123     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2124     type: false,
2125     /**
2126      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2127      */
2128     registerMenu : true,
2129     
2130     menuItems :false, // stores the menu items..
2131     
2132     hidden:true,
2133         
2134     parentMenu : false,
2135     
2136     stopEvent : true,
2137     
2138     isLink : false,
2139     
2140     getChildContainer : function() {
2141         return this.el;  
2142     },
2143     
2144     getAutoCreate : function(){
2145          
2146         //if (['right'].indexOf(this.align)!==-1) {
2147         //    cfg.cn[1].cls += ' pull-right'
2148         //}
2149         
2150         
2151         var cfg = {
2152             tag : 'ul',
2153             cls : 'dropdown-menu' ,
2154             style : 'z-index:1000'
2155             
2156         };
2157         
2158         if (this.type === 'submenu') {
2159             cfg.cls = 'submenu active';
2160         }
2161         if (this.type === 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         return cfg;
2166     },
2167     initEvents : function() {
2168         
2169        // Roo.log("ADD event");
2170        // Roo.log(this.triggerEl.dom);
2171         
2172         this.triggerEl.on('click', this.onTriggerClick, this);
2173         
2174         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2175         
2176         
2177         if (this.triggerEl.hasClass('nav-item')) {
2178             // dropdown toggle on the 'a' in BS4?
2179             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2180         } else {
2181             this.triggerEl.addClass('dropdown-toggle');
2182         }
2183         if (Roo.isTouch) {
2184             this.el.on('touchstart'  , this.onTouch, this);
2185         }
2186         this.el.on('click' , this.onClick, this);
2187
2188         this.el.on("mouseover", this.onMouseOver, this);
2189         this.el.on("mouseout", this.onMouseOut, this);
2190         
2191     },
2192     
2193     findTargetItem : function(e)
2194     {
2195         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2196         if(!t){
2197             return false;
2198         }
2199         //Roo.log(t);         Roo.log(t.id);
2200         if(t && t.id){
2201             //Roo.log(this.menuitems);
2202             return this.menuitems.get(t.id);
2203             
2204             //return this.items.get(t.menuItemId);
2205         }
2206         
2207         return false;
2208     },
2209     
2210     onTouch : function(e) 
2211     {
2212         Roo.log("menu.onTouch");
2213         //e.stopEvent(); this make the user popdown broken
2214         this.onClick(e);
2215     },
2216     
2217     onClick : function(e)
2218     {
2219         Roo.log("menu.onClick");
2220         
2221         var t = this.findTargetItem(e);
2222         if(!t || t.isContainer){
2223             return;
2224         }
2225         Roo.log(e);
2226         /*
2227         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2228             if(t == this.activeItem && t.shouldDeactivate(e)){
2229                 this.activeItem.deactivate();
2230                 delete this.activeItem;
2231                 return;
2232             }
2233             if(t.canActivate){
2234                 this.setActiveItem(t, true);
2235             }
2236             return;
2237             
2238             
2239         }
2240         */
2241        
2242         Roo.log('pass click event');
2243         
2244         t.onClick(e);
2245         
2246         this.fireEvent("click", this, t, e);
2247         
2248         var _this = this;
2249         
2250         if(!t.href.length || t.href == '#'){
2251             (function() { _this.hide(); }).defer(100);
2252         }
2253         
2254     },
2255     
2256     onMouseOver : function(e){
2257         var t  = this.findTargetItem(e);
2258         //Roo.log(t);
2259         //if(t){
2260         //    if(t.canActivate && !t.disabled){
2261         //        this.setActiveItem(t, true);
2262         //    }
2263         //}
2264         
2265         this.fireEvent("mouseover", this, e, t);
2266     },
2267     isVisible : function(){
2268         return !this.hidden;
2269     },
2270      onMouseOut : function(e){
2271         var t  = this.findTargetItem(e);
2272         
2273         //if(t ){
2274         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2275         //        this.activeItem.deactivate();
2276         //        delete this.activeItem;
2277         //    }
2278         //}
2279         this.fireEvent("mouseout", this, e, t);
2280     },
2281     
2282     
2283     /**
2284      * Displays this menu relative to another element
2285      * @param {String/HTMLElement/Roo.Element} element The element to align to
2286      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287      * the element (defaults to this.defaultAlign)
2288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2289      */
2290     show : function(el, pos, parentMenu){
2291         this.parentMenu = parentMenu;
2292         if(!this.el){
2293             this.render();
2294         }
2295         this.fireEvent("beforeshow", this);
2296         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2297     },
2298      /**
2299      * Displays this menu at a specific xy position
2300      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2302      */
2303     showAt : function(xy, parentMenu, /* private: */_e){
2304         this.parentMenu = parentMenu;
2305         if(!this.el){
2306             this.render();
2307         }
2308         if(_e !== false){
2309             this.fireEvent("beforeshow", this);
2310             //xy = this.el.adjustForConstraints(xy);
2311         }
2312         
2313         //this.el.show();
2314         this.hideMenuItems();
2315         this.hidden = false;
2316         this.triggerEl.addClass('open');
2317         this.el.addClass('show');
2318         
2319         // reassign x when hitting right
2320         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2322         }
2323         
2324         // reassign y when hitting bottom
2325         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2327         }
2328         
2329         // but the list may align on trigger left or trigger top... should it be a properity?
2330         
2331         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2332             this.el.setXY(xy);
2333         }
2334         
2335         this.focus();
2336         this.fireEvent("show", this);
2337     },
2338     
2339     focus : function(){
2340         return;
2341         if(!this.hidden){
2342             this.doFocus.defer(50, this);
2343         }
2344     },
2345
2346     doFocus : function(){
2347         if(!this.hidden){
2348             this.focusEl.focus();
2349         }
2350     },
2351
2352     /**
2353      * Hides this menu and optionally all parent menus
2354      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2355      */
2356     hide : function(deep)
2357     {
2358         
2359         this.hideMenuItems();
2360         if(this.el && this.isVisible()){
2361             this.fireEvent("beforehide", this);
2362             if(this.activeItem){
2363                 this.activeItem.deactivate();
2364                 this.activeItem = null;
2365             }
2366             this.triggerEl.removeClass('open');;
2367             this.el.removeClass('show');
2368             this.hidden = true;
2369             this.fireEvent("hide", this);
2370         }
2371         if(deep === true && this.parentMenu){
2372             this.parentMenu.hide(true);
2373         }
2374     },
2375     
2376     onTriggerClick : function(e)
2377     {
2378         Roo.log('trigger click');
2379         
2380         var target = e.getTarget();
2381         
2382         Roo.log(target.nodeName.toLowerCase());
2383         
2384         if(target.nodeName.toLowerCase() === 'i'){
2385             e.preventDefault();
2386         }
2387         
2388     },
2389     
2390     onTriggerPress  : function(e)
2391     {
2392         Roo.log('trigger press');
2393         //Roo.log(e.getTarget());
2394        // Roo.log(this.triggerEl.dom);
2395        
2396         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397         var pel = Roo.get(e.getTarget());
2398         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399             Roo.log('is treeview or dropdown?');
2400             return;
2401         }
2402         
2403         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2404             return;
2405         }
2406         
2407         if (this.isVisible()) {
2408             Roo.log('hide');
2409             this.hide();
2410         } else {
2411             Roo.log('show');
2412             this.show(this.triggerEl, false, false);
2413         }
2414         
2415         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2416             e.stopEvent();
2417         }
2418         
2419     },
2420        
2421     
2422     hideMenuItems : function()
2423     {
2424         Roo.log("hide Menu Items");
2425         if (!this.el) { 
2426             return;
2427         }
2428         //$(backdrop).remove()
2429         this.el.select('.open',true).each(function(aa) {
2430             
2431             aa.removeClass('open');
2432           //var parent = getParent($(this))
2433           //var relatedTarget = { relatedTarget: this }
2434           
2435            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436           //if (e.isDefaultPrevented()) return
2437            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2438         });
2439     },
2440     addxtypeChild : function (tree, cntr) {
2441         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2442           
2443         this.menuitems.add(comp);
2444         return comp;
2445
2446     },
2447     getEl : function()
2448     {
2449         Roo.log(this.el);
2450         return this.el;
2451     },
2452     
2453     clear : function()
2454     {
2455         this.getEl().dom.innerHTML = '';
2456         this.menuitems.clear();
2457     }
2458 });
2459
2460  
2461  /*
2462  * - LGPL
2463  *
2464  * menu item
2465  * 
2466  */
2467
2468
2469 /**
2470  * @class Roo.bootstrap.MenuItem
2471  * @extends Roo.bootstrap.Component
2472  * Bootstrap MenuItem class
2473  * @cfg {String} html the menu label
2474  * @cfg {String} href the link
2475  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2478  * @cfg {String} fa favicon to show on left of menu item.
2479  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2480  * 
2481  * 
2482  * @constructor
2483  * Create a new MenuItem
2484  * @param {Object} config The config object
2485  */
2486
2487
2488 Roo.bootstrap.MenuItem = function(config){
2489     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2490     this.addEvents({
2491         // raw events
2492         /**
2493          * @event click
2494          * The raw click event for the entire grid.
2495          * @param {Roo.bootstrap.MenuItem} this
2496          * @param {Roo.EventObject} e
2497          */
2498         "click" : true
2499     });
2500 };
2501
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2503     
2504     href : false,
2505     html : false,
2506     preventDefault: false,
2507     isContainer : false,
2508     active : false,
2509     fa: false,
2510     
2511     getAutoCreate : function(){
2512         
2513         if(this.isContainer){
2514             return {
2515                 tag: 'li',
2516                 cls: 'dropdown-menu-item '
2517             };
2518         }
2519         var ctag = {
2520             tag: 'span',
2521             html: 'Link'
2522         };
2523         
2524         var anc = {
2525             tag : 'a',
2526             cls : 'dropdown-item',
2527             href : '#',
2528             cn : [  ]
2529         };
2530         
2531         if (this.fa !== false) {
2532             anc.cn.push({
2533                 tag : 'i',
2534                 cls : 'fa fa-' + this.fa
2535             });
2536         }
2537         
2538         anc.cn.push(ctag);
2539         
2540         
2541         var cfg= {
2542             tag: 'li',
2543             cls: 'dropdown-menu-item',
2544             cn: [ anc ]
2545         };
2546         if (this.parent().type == 'treeview') {
2547             cfg.cls = 'treeview-menu';
2548         }
2549         if (this.active) {
2550             cfg.cls += ' active';
2551         }
2552         
2553         
2554         
2555         anc.href = this.href || cfg.cn[0].href ;
2556         ctag.html = this.html || cfg.cn[0].html ;
2557         return cfg;
2558     },
2559     
2560     initEvents: function()
2561     {
2562         if (this.parent().type == 'treeview') {
2563             this.el.select('a').on('click', this.onClick, this);
2564         }
2565         
2566         if (this.menu) {
2567             this.menu.parentType = this.xtype;
2568             this.menu.triggerEl = this.el;
2569             this.menu = this.addxtype(Roo.apply({}, this.menu));
2570         }
2571         
2572     },
2573     onClick : function(e)
2574     {
2575         Roo.log('item on click ');
2576         
2577         if(this.preventDefault){
2578             e.preventDefault();
2579         }
2580         //this.parent().hideMenuItems();
2581         
2582         this.fireEvent('click', this, e);
2583     },
2584     getEl : function()
2585     {
2586         return this.el;
2587     } 
2588 });
2589
2590  
2591
2592  /*
2593  * - LGPL
2594  *
2595  * menu separator
2596  * 
2597  */
2598
2599
2600 /**
2601  * @class Roo.bootstrap.MenuSeparator
2602  * @extends Roo.bootstrap.Component
2603  * Bootstrap MenuSeparator class
2604  * 
2605  * @constructor
2606  * Create a new MenuItem
2607  * @param {Object} config The config object
2608  */
2609
2610
2611 Roo.bootstrap.MenuSeparator = function(config){
2612     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2613 };
2614
2615 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2616     
2617     getAutoCreate : function(){
2618         var cfg = {
2619             cls: 'divider',
2620             tag : 'li'
2621         };
2622         
2623         return cfg;
2624     }
2625    
2626 });
2627
2628  
2629
2630  
2631 /*
2632 * Licence: LGPL
2633 */
2634
2635 /**
2636  * @class Roo.bootstrap.Modal
2637  * @extends Roo.bootstrap.Component
2638  * Bootstrap Modal class
2639  * @cfg {String} title Title of dialog
2640  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2641  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2642  * @cfg {Boolean} specificTitle default false
2643  * @cfg {Array} buttons Array of buttons or standard button set..
2644  * @cfg {String} buttonPosition (left|right|center) default right
2645  * @cfg {Boolean} animate default true
2646  * @cfg {Boolean} allow_close default true
2647  * @cfg {Boolean} fitwindow default false
2648  * @cfg {String} size (sm|lg) default empty
2649  * @cfg {Number} max_width set the max width of modal
2650  *
2651  *
2652  * @constructor
2653  * Create a new Modal Dialog
2654  * @param {Object} config The config object
2655  */
2656
2657 Roo.bootstrap.Modal = function(config){
2658     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2659     this.addEvents({
2660         // raw events
2661         /**
2662          * @event btnclick
2663          * The raw btnclick event for the button
2664          * @param {Roo.EventObject} e
2665          */
2666         "btnclick" : true,
2667         /**
2668          * @event resize
2669          * Fire when dialog resize
2670          * @param {Roo.bootstrap.Modal} this
2671          * @param {Roo.EventObject} e
2672          */
2673         "resize" : true
2674     });
2675     this.buttons = this.buttons || [];
2676
2677     if (this.tmpl) {
2678         this.tmpl = Roo.factory(this.tmpl);
2679     }
2680
2681 };
2682
2683 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2684
2685     title : 'test dialog',
2686
2687     buttons : false,
2688
2689     // set on load...
2690
2691     html: false,
2692
2693     tmp: false,
2694
2695     specificTitle: false,
2696
2697     buttonPosition: 'right',
2698
2699     allow_close : true,
2700
2701     animate : true,
2702
2703     fitwindow: false,
2704     
2705      // private
2706     dialogEl: false,
2707     bodyEl:  false,
2708     footerEl:  false,
2709     titleEl:  false,
2710     closeEl:  false,
2711
2712     size: '',
2713     
2714     max_width: 0,
2715     
2716     max_height: 0,
2717     
2718     fit_content: false,
2719
2720     onRender : function(ct, position)
2721     {
2722         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2723
2724         if(!this.el){
2725             var cfg = Roo.apply({},  this.getAutoCreate());
2726             cfg.id = Roo.id();
2727             //if(!cfg.name){
2728             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2729             //}
2730             //if (!cfg.name.length) {
2731             //    delete cfg.name;
2732            // }
2733             if (this.cls) {
2734                 cfg.cls += ' ' + this.cls;
2735             }
2736             if (this.style) {
2737                 cfg.style = this.style;
2738             }
2739             this.el = Roo.get(document.body).createChild(cfg, position);
2740         }
2741         //var type = this.el.dom.type;
2742
2743
2744         if(this.tabIndex !== undefined){
2745             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2746         }
2747
2748         this.dialogEl = this.el.select('.modal-dialog',true).first();
2749         this.bodyEl = this.el.select('.modal-body',true).first();
2750         this.closeEl = this.el.select('.modal-header .close', true).first();
2751         this.headerEl = this.el.select('.modal-header',true).first();
2752         this.titleEl = this.el.select('.modal-title',true).first();
2753         this.footerEl = this.el.select('.modal-footer',true).first();
2754
2755         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2756         
2757         //this.el.addClass("x-dlg-modal");
2758
2759         if (this.buttons.length) {
2760             Roo.each(this.buttons, function(bb) {
2761                 var b = Roo.apply({}, bb);
2762                 b.xns = b.xns || Roo.bootstrap;
2763                 b.xtype = b.xtype || 'Button';
2764                 if (typeof(b.listeners) == 'undefined') {
2765                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2766                 }
2767
2768                 var btn = Roo.factory(b);
2769
2770                 btn.render(this.el.select('.modal-footer div').first());
2771
2772             },this);
2773         }
2774         // render the children.
2775         var nitems = [];
2776
2777         if(typeof(this.items) != 'undefined'){
2778             var items = this.items;
2779             delete this.items;
2780
2781             for(var i =0;i < items.length;i++) {
2782                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2783             }
2784         }
2785
2786         this.items = nitems;
2787
2788         // where are these used - they used to be body/close/footer
2789
2790
2791         this.initEvents();
2792         //this.el.addClass([this.fieldClass, this.cls]);
2793
2794     },
2795
2796     getAutoCreate : function()
2797     {
2798         var bdy = {
2799                 cls : 'modal-body',
2800                 html : this.html || ''
2801         };
2802
2803         var title = {
2804             tag: 'h4',
2805             cls : 'modal-title',
2806             html : this.title
2807         };
2808
2809         if(this.specificTitle){
2810             title = this.title;
2811
2812         };
2813
2814         var header = [];
2815         if (this.allow_close && Roo.bootstrap.version == 3) {
2816             header.push({
2817                 tag: 'button',
2818                 cls : 'close',
2819                 html : '&times'
2820             });
2821         }
2822
2823         header.push(title);
2824
2825         if (this.allow_close && Roo.bootstrap.version == 4) {
2826             header.push({
2827                 tag: 'button',
2828                 cls : 'close',
2829                 html : '&times'
2830             });
2831         }
2832         
2833         var size = '';
2834
2835         if(this.size.length){
2836             size = 'modal-' + this.size;
2837         }
2838
2839         var modal = {
2840             cls: "modal",
2841              cn : [
2842                 {
2843                     cls: "modal-dialog " + size,
2844                     cn : [
2845                         {
2846                             cls : "modal-content",
2847                             cn : [
2848                                 {
2849                                     cls : 'modal-header',
2850                                     cn : header
2851                                 },
2852                                 bdy,
2853                                 {
2854                                     cls : 'modal-footer',
2855                                     cn : [
2856                                         {
2857                                             tag: 'div',
2858                                             cls: 'btn-' + this.buttonPosition
2859                                         }
2860                                     ]
2861
2862                                 }
2863
2864
2865                             ]
2866
2867                         }
2868                     ]
2869
2870                 }
2871             ]
2872         };
2873
2874         if(this.animate){
2875             modal.cls += ' fade';
2876         }
2877
2878         return modal;
2879
2880     },
2881     getChildContainer : function() {
2882
2883          return this.bodyEl;
2884
2885     },
2886     getButtonContainer : function() {
2887          return this.el.select('.modal-footer div',true).first();
2888
2889     },
2890     initEvents : function()
2891     {
2892         if (this.allow_close) {
2893             this.closeEl.on('click', this.hide, this);
2894         }
2895         Roo.EventManager.onWindowResize(this.resize, this, true);
2896
2897
2898     },
2899
2900     resize : function()
2901     {
2902         this.maskEl.setSize(
2903             Roo.lib.Dom.getViewWidth(true),
2904             Roo.lib.Dom.getViewHeight(true)
2905         );
2906         
2907         if (this.fitwindow) {
2908             this.setSize(
2909                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2910                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2911             );
2912             return;
2913         }
2914         
2915         if(this.max_width !== 0) {
2916             
2917             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2918             
2919             if(this.height) {
2920                 this.setSize(w, this.height);
2921                 return;
2922             }
2923             
2924             if(this.max_height) {
2925                 this.setSize(w,Math.min(
2926                     this.max_height,
2927                     Roo.lib.Dom.getViewportHeight(true) - 60
2928                 ));
2929                 
2930                 return;
2931             }
2932             
2933             if(!this.fit_content) {
2934                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2935                 return;
2936             }
2937             
2938             this.setSize(w, Math.min(
2939                 60 +
2940                 this.headerEl.getHeight() + 
2941                 this.footerEl.getHeight() + 
2942                 this.getChildHeight(this.bodyEl.dom.childNodes),
2943                 Roo.lib.Dom.getViewportHeight(true) - 60)
2944             );
2945         }
2946         
2947     },
2948
2949     setSize : function(w,h)
2950     {
2951         if (!w && !h) {
2952             return;
2953         }
2954         
2955         this.resizeTo(w,h);
2956     },
2957
2958     show : function() {
2959
2960         if (!this.rendered) {
2961             this.render();
2962         }
2963
2964         //this.el.setStyle('display', 'block');
2965         this.el.removeClass('hideing');
2966         this.el.dom.style.display='block';
2967         
2968         Roo.get(document.body).addClass('modal-open');
2969  
2970         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2971             var _this = this;
2972             (function(){
2973                 this.el.addClass('show');
2974                 this.el.addClass('in');
2975             }).defer(50, this);
2976         }else{
2977             this.el.addClass('show');
2978             this.el.addClass('in');
2979         }
2980
2981         // not sure how we can show data in here..
2982         //if (this.tmpl) {
2983         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2984         //}
2985
2986         Roo.get(document.body).addClass("x-body-masked");
2987         
2988         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2989         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2990         this.maskEl.dom.style.display = 'block';
2991         this.maskEl.addClass('show');
2992         
2993         
2994         this.resize();
2995         
2996         this.fireEvent('show', this);
2997
2998         // set zindex here - otherwise it appears to be ignored...
2999         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3000
3001         (function () {
3002             this.items.forEach( function(e) {
3003                 e.layout ? e.layout() : false;
3004
3005             });
3006         }).defer(100,this);
3007
3008     },
3009     hide : function()
3010     {
3011         if(this.fireEvent("beforehide", this) !== false){
3012             
3013             this.maskEl.removeClass('show');
3014             
3015             this.maskEl.dom.style.display = '';
3016             Roo.get(document.body).removeClass("x-body-masked");
3017             this.el.removeClass('in');
3018             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3019
3020             if(this.animate){ // why
3021                 this.el.addClass('hideing');
3022                 this.el.removeClass('show');
3023                 (function(){
3024                     if (!this.el.hasClass('hideing')) {
3025                         return; // it's been shown again...
3026                     }
3027                     
3028                     this.el.dom.style.display='';
3029
3030                     Roo.get(document.body).removeClass('modal-open');
3031                     this.el.removeClass('hideing');
3032                 }).defer(150,this);
3033                 
3034             }else{
3035                 this.el.removeClass('show');
3036                 this.el.dom.style.display='';
3037                 Roo.get(document.body).removeClass('modal-open');
3038
3039             }
3040             this.fireEvent('hide', this);
3041         }
3042     },
3043     isVisible : function()
3044     {
3045         
3046         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3047         
3048     },
3049
3050     addButton : function(str, cb)
3051     {
3052
3053
3054         var b = Roo.apply({}, { html : str } );
3055         b.xns = b.xns || Roo.bootstrap;
3056         b.xtype = b.xtype || 'Button';
3057         if (typeof(b.listeners) == 'undefined') {
3058             b.listeners = { click : cb.createDelegate(this)  };
3059         }
3060
3061         var btn = Roo.factory(b);
3062
3063         btn.render(this.el.select('.modal-footer div').first());
3064
3065         return btn;
3066
3067     },
3068
3069     setDefaultButton : function(btn)
3070     {
3071         //this.el.select('.modal-footer').()
3072     },
3073     diff : false,
3074
3075     resizeTo: function(w,h)
3076     {
3077         // skip.. ?? why??
3078
3079         this.dialogEl.setWidth(w);
3080         if (this.diff === false) {
3081             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3082         }
3083
3084         this.bodyEl.setHeight(h - this.diff);
3085
3086         this.fireEvent('resize', this);
3087
3088     },
3089     setContentSize  : function(w, h)
3090     {
3091
3092     },
3093     onButtonClick: function(btn,e)
3094     {
3095         //Roo.log([a,b,c]);
3096         this.fireEvent('btnclick', btn.name, e);
3097     },
3098      /**
3099      * Set the title of the Dialog
3100      * @param {String} str new Title
3101      */
3102     setTitle: function(str) {
3103         this.titleEl.dom.innerHTML = str;
3104     },
3105     /**
3106      * Set the body of the Dialog
3107      * @param {String} str new Title
3108      */
3109     setBody: function(str) {
3110         this.bodyEl.dom.innerHTML = str;
3111     },
3112     /**
3113      * Set the body of the Dialog using the template
3114      * @param {Obj} data - apply this data to the template and replace the body contents.
3115      */
3116     applyBody: function(obj)
3117     {
3118         if (!this.tmpl) {
3119             Roo.log("Error - using apply Body without a template");
3120             //code
3121         }
3122         this.tmpl.overwrite(this.bodyEl, obj);
3123     },
3124     
3125     getChildHeight : function(child_nodes)
3126     {
3127         if(
3128             !child_nodes ||
3129             child_nodes.length == 0
3130         ) {
3131             return;
3132         }
3133         
3134         var child_height = 0;
3135         
3136         for(var i = 0; i < child_nodes.length; i++) {
3137             
3138             /*
3139             * for modal with tabs...
3140             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3141                 
3142                 var layout_childs = child_nodes[i].childNodes;
3143                 
3144                 for(var j = 0; j < layout_childs.length; j++) {
3145                     
3146                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3147                         
3148                         var layout_body_childs = layout_childs[j].childNodes;
3149                         
3150                         for(var k = 0; k < layout_body_childs.length; k++) {
3151                             
3152                             if(layout_body_childs[k].classList.contains('navbar')) {
3153                                 child_height += layout_body_childs[k].offsetHeight;
3154                                 continue;
3155                             }
3156                             
3157                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3158                                 
3159                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3160                                 
3161                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3162                                     
3163                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3164                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3165                                         continue;
3166                                     }
3167                                     
3168                                 }
3169                                 
3170                             }
3171                             
3172                         }
3173                     }
3174                 }
3175                 continue;
3176             }
3177             */
3178             
3179             child_height += child_nodes[i].offsetHeight;
3180             // Roo.log(child_nodes[i].offsetHeight);
3181         }
3182         
3183         return child_height;
3184     }
3185
3186 });
3187
3188
3189 Roo.apply(Roo.bootstrap.Modal,  {
3190     /**
3191          * Button config that displays a single OK button
3192          * @type Object
3193          */
3194         OK :  [{
3195             name : 'ok',
3196             weight : 'primary',
3197             html : 'OK'
3198         }],
3199         /**
3200          * Button config that displays Yes and No buttons
3201          * @type Object
3202          */
3203         YESNO : [
3204             {
3205                 name  : 'no',
3206                 html : 'No'
3207             },
3208             {
3209                 name  :'yes',
3210                 weight : 'primary',
3211                 html : 'Yes'
3212             }
3213         ],
3214
3215         /**
3216          * Button config that displays OK and Cancel buttons
3217          * @type Object
3218          */
3219         OKCANCEL : [
3220             {
3221                name : 'cancel',
3222                 html : 'Cancel'
3223             },
3224             {
3225                 name : 'ok',
3226                 weight : 'primary',
3227                 html : 'OK'
3228             }
3229         ],
3230         /**
3231          * Button config that displays Yes, No and Cancel buttons
3232          * @type Object
3233          */
3234         YESNOCANCEL : [
3235             {
3236                 name : 'yes',
3237                 weight : 'primary',
3238                 html : 'Yes'
3239             },
3240             {
3241                 name : 'no',
3242                 html : 'No'
3243             },
3244             {
3245                 name : 'cancel',
3246                 html : 'Cancel'
3247             }
3248         ],
3249         
3250         zIndex : 10001
3251 });
3252 /*
3253  * - LGPL
3254  *
3255  * messagebox - can be used as a replace
3256  * 
3257  */
3258 /**
3259  * @class Roo.MessageBox
3260  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3261  * Example usage:
3262  *<pre><code>
3263 // Basic alert:
3264 Roo.Msg.alert('Status', 'Changes saved successfully.');
3265
3266 // Prompt for user data:
3267 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3268     if (btn == 'ok'){
3269         // process text value...
3270     }
3271 });
3272
3273 // Show a dialog using config options:
3274 Roo.Msg.show({
3275    title:'Save Changes?',
3276    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3277    buttons: Roo.Msg.YESNOCANCEL,
3278    fn: processResult,
3279    animEl: 'elId'
3280 });
3281 </code></pre>
3282  * @singleton
3283  */
3284 Roo.bootstrap.MessageBox = function(){
3285     var dlg, opt, mask, waitTimer;
3286     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3287     var buttons, activeTextEl, bwidth;
3288
3289     
3290     // private
3291     var handleButton = function(button){
3292         dlg.hide();
3293         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3294     };
3295
3296     // private
3297     var handleHide = function(){
3298         if(opt && opt.cls){
3299             dlg.el.removeClass(opt.cls);
3300         }
3301         //if(waitTimer){
3302         //    Roo.TaskMgr.stop(waitTimer);
3303         //    waitTimer = null;
3304         //}
3305     };
3306
3307     // private
3308     var updateButtons = function(b){
3309         var width = 0;
3310         if(!b){
3311             buttons["ok"].hide();
3312             buttons["cancel"].hide();
3313             buttons["yes"].hide();
3314             buttons["no"].hide();
3315             //dlg.footer.dom.style.display = 'none';
3316             return width;
3317         }
3318         dlg.footerEl.dom.style.display = '';
3319         for(var k in buttons){
3320             if(typeof buttons[k] != "function"){
3321                 if(b[k]){
3322                     buttons[k].show();
3323                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3324                     width += buttons[k].el.getWidth()+15;
3325                 }else{
3326                     buttons[k].hide();
3327                 }
3328             }
3329         }
3330         return width;
3331     };
3332
3333     // private
3334     var handleEsc = function(d, k, e){
3335         if(opt && opt.closable !== false){
3336             dlg.hide();
3337         }
3338         if(e){
3339             e.stopEvent();
3340         }
3341     };
3342
3343     return {
3344         /**
3345          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3346          * @return {Roo.BasicDialog} The BasicDialog element
3347          */
3348         getDialog : function(){
3349            if(!dlg){
3350                 dlg = new Roo.bootstrap.Modal( {
3351                     //draggable: true,
3352                     //resizable:false,
3353                     //constraintoviewport:false,
3354                     //fixedcenter:true,
3355                     //collapsible : false,
3356                     //shim:true,
3357                     //modal: true,
3358                 //    width: 'auto',
3359                   //  height:100,
3360                     //buttonAlign:"center",
3361                     closeClick : function(){
3362                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3363                             handleButton("no");
3364                         }else{
3365                             handleButton("cancel");
3366                         }
3367                     }
3368                 });
3369                 dlg.render();
3370                 dlg.on("hide", handleHide);
3371                 mask = dlg.mask;
3372                 //dlg.addKeyListener(27, handleEsc);
3373                 buttons = {};
3374                 this.buttons = buttons;
3375                 var bt = this.buttonText;
3376                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3377                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3378                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3379                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3380                 //Roo.log(buttons);
3381                 bodyEl = dlg.bodyEl.createChild({
3382
3383                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3384                         '<textarea class="roo-mb-textarea"></textarea>' +
3385                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3386                 });
3387                 msgEl = bodyEl.dom.firstChild;
3388                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3389                 textboxEl.enableDisplayMode();
3390                 textboxEl.addKeyListener([10,13], function(){
3391                     if(dlg.isVisible() && opt && opt.buttons){
3392                         if(opt.buttons.ok){
3393                             handleButton("ok");
3394                         }else if(opt.buttons.yes){
3395                             handleButton("yes");
3396                         }
3397                     }
3398                 });
3399                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3400                 textareaEl.enableDisplayMode();
3401                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3402                 progressEl.enableDisplayMode();
3403                 
3404                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3405                 var pf = progressEl.dom.firstChild;
3406                 if (pf) {
3407                     pp = Roo.get(pf.firstChild);
3408                     pp.setHeight(pf.offsetHeight);
3409                 }
3410                 
3411             }
3412             return dlg;
3413         },
3414
3415         /**
3416          * Updates the message box body text
3417          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3418          * the XHTML-compliant non-breaking space character '&amp;#160;')
3419          * @return {Roo.MessageBox} This message box
3420          */
3421         updateText : function(text)
3422         {
3423             if(!dlg.isVisible() && !opt.width){
3424                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3425                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3426             }
3427             msgEl.innerHTML = text || '&#160;';
3428       
3429             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3430             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3431             var w = Math.max(
3432                     Math.min(opt.width || cw , this.maxWidth), 
3433                     Math.max(opt.minWidth || this.minWidth, bwidth)
3434             );
3435             if(opt.prompt){
3436                 activeTextEl.setWidth(w);
3437             }
3438             if(dlg.isVisible()){
3439                 dlg.fixedcenter = false;
3440             }
3441             // to big, make it scroll. = But as usual stupid IE does not support
3442             // !important..
3443             
3444             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3445                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3446                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3447             } else {
3448                 bodyEl.dom.style.height = '';
3449                 bodyEl.dom.style.overflowY = '';
3450             }
3451             if (cw > w) {
3452                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3453             } else {
3454                 bodyEl.dom.style.overflowX = '';
3455             }
3456             
3457             dlg.setContentSize(w, bodyEl.getHeight());
3458             if(dlg.isVisible()){
3459                 dlg.fixedcenter = true;
3460             }
3461             return this;
3462         },
3463
3464         /**
3465          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3466          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3467          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3468          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3469          * @return {Roo.MessageBox} This message box
3470          */
3471         updateProgress : function(value, text){
3472             if(text){
3473                 this.updateText(text);
3474             }
3475             
3476             if (pp) { // weird bug on my firefox - for some reason this is not defined
3477                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3478                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3479             }
3480             return this;
3481         },        
3482
3483         /**
3484          * Returns true if the message box is currently displayed
3485          * @return {Boolean} True if the message box is visible, else false
3486          */
3487         isVisible : function(){
3488             return dlg && dlg.isVisible();  
3489         },
3490
3491         /**
3492          * Hides the message box if it is displayed
3493          */
3494         hide : function(){
3495             if(this.isVisible()){
3496                 dlg.hide();
3497             }  
3498         },
3499
3500         /**
3501          * Displays a new message box, or reinitializes an existing message box, based on the config options
3502          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3503          * The following config object properties are supported:
3504          * <pre>
3505 Property    Type             Description
3506 ----------  ---------------  ------------------------------------------------------------------------------------
3507 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3508                                    closes (defaults to undefined)
3509 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3510                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3511 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3512                                    progress and wait dialogs will ignore this property and always hide the
3513                                    close button as they can only be closed programmatically.
3514 cls               String           A custom CSS class to apply to the message box element
3515 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3516                                    displayed (defaults to 75)
3517 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3518                                    function will be btn (the name of the button that was clicked, if applicable,
3519                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3520                                    Progress and wait dialogs will ignore this option since they do not respond to
3521                                    user actions and can only be closed programmatically, so any required function
3522                                    should be called by the same code after it closes the dialog.
3523 icon              String           A CSS class that provides a background image to be used as an icon for
3524                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3525 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3526 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3527 modal             Boolean          False to allow user interaction with the page while the message box is
3528                                    displayed (defaults to true)
3529 msg               String           A string that will replace the existing message box body text (defaults
3530                                    to the XHTML-compliant non-breaking space character '&#160;')
3531 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3532 progress          Boolean          True to display a progress bar (defaults to false)
3533 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3534 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3535 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3536 title             String           The title text
3537 value             String           The string value to set into the active textbox element if displayed
3538 wait              Boolean          True to display a progress bar (defaults to false)
3539 width             Number           The width of the dialog in pixels
3540 </pre>
3541          *
3542          * Example usage:
3543          * <pre><code>
3544 Roo.Msg.show({
3545    title: 'Address',
3546    msg: 'Please enter your address:',
3547    width: 300,
3548    buttons: Roo.MessageBox.OKCANCEL,
3549    multiline: true,
3550    fn: saveAddress,
3551    animEl: 'addAddressBtn'
3552 });
3553 </code></pre>
3554          * @param {Object} config Configuration options
3555          * @return {Roo.MessageBox} This message box
3556          */
3557         show : function(options)
3558         {
3559             
3560             // this causes nightmares if you show one dialog after another
3561             // especially on callbacks..
3562              
3563             if(this.isVisible()){
3564                 
3565                 this.hide();
3566                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3567                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3568                 Roo.log("New Dialog Message:" +  options.msg )
3569                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3570                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3571                 
3572             }
3573             var d = this.getDialog();
3574             opt = options;
3575             d.setTitle(opt.title || "&#160;");
3576             d.closeEl.setDisplayed(opt.closable !== false);
3577             activeTextEl = textboxEl;
3578             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3579             if(opt.prompt){
3580                 if(opt.multiline){
3581                     textboxEl.hide();
3582                     textareaEl.show();
3583                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3584                         opt.multiline : this.defaultTextHeight);
3585                     activeTextEl = textareaEl;
3586                 }else{
3587                     textboxEl.show();
3588                     textareaEl.hide();
3589                 }
3590             }else{
3591                 textboxEl.hide();
3592                 textareaEl.hide();
3593             }
3594             progressEl.setDisplayed(opt.progress === true);
3595             this.updateProgress(0);
3596             activeTextEl.dom.value = opt.value || "";
3597             if(opt.prompt){
3598                 dlg.setDefaultButton(activeTextEl);
3599             }else{
3600                 var bs = opt.buttons;
3601                 var db = null;
3602                 if(bs && bs.ok){
3603                     db = buttons["ok"];
3604                 }else if(bs && bs.yes){
3605                     db = buttons["yes"];
3606                 }
3607                 dlg.setDefaultButton(db);
3608             }
3609             bwidth = updateButtons(opt.buttons);
3610             this.updateText(opt.msg);
3611             if(opt.cls){
3612                 d.el.addClass(opt.cls);
3613             }
3614             d.proxyDrag = opt.proxyDrag === true;
3615             d.modal = opt.modal !== false;
3616             d.mask = opt.modal !== false ? mask : false;
3617             if(!d.isVisible()){
3618                 // force it to the end of the z-index stack so it gets a cursor in FF
3619                 document.body.appendChild(dlg.el.dom);
3620                 d.animateTarget = null;
3621                 d.show(options.animEl);
3622             }
3623             return this;
3624         },
3625
3626         /**
3627          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3628          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3629          * and closing the message box when the process is complete.
3630          * @param {String} title The title bar text
3631          * @param {String} msg The message box body text
3632          * @return {Roo.MessageBox} This message box
3633          */
3634         progress : function(title, msg){
3635             this.show({
3636                 title : title,
3637                 msg : msg,
3638                 buttons: false,
3639                 progress:true,
3640                 closable:false,
3641                 minWidth: this.minProgressWidth,
3642                 modal : true
3643             });
3644             return this;
3645         },
3646
3647         /**
3648          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3649          * If a callback function is passed it will be called after the user clicks the button, and the
3650          * id of the button that was clicked will be passed as the only parameter to the callback
3651          * (could also be the top-right close button).
3652          * @param {String} title The title bar text
3653          * @param {String} msg The message box body text
3654          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3655          * @param {Object} scope (optional) The scope of the callback function
3656          * @return {Roo.MessageBox} This message box
3657          */
3658         alert : function(title, msg, fn, scope)
3659         {
3660             this.show({
3661                 title : title,
3662                 msg : msg,
3663                 buttons: this.OK,
3664                 fn: fn,
3665                 closable : false,
3666                 scope : scope,
3667                 modal : true
3668             });
3669             return this;
3670         },
3671
3672         /**
3673          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3674          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3675          * You are responsible for closing the message box when the process is complete.
3676          * @param {String} msg The message box body text
3677          * @param {String} title (optional) The title bar text
3678          * @return {Roo.MessageBox} This message box
3679          */
3680         wait : function(msg, title){
3681             this.show({
3682                 title : title,
3683                 msg : msg,
3684                 buttons: false,
3685                 closable:false,
3686                 progress:true,
3687                 modal:true,
3688                 width:300,
3689                 wait:true
3690             });
3691             waitTimer = Roo.TaskMgr.start({
3692                 run: function(i){
3693                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3694                 },
3695                 interval: 1000
3696             });
3697             return this;
3698         },
3699
3700         /**
3701          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3702          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3703          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3704          * @param {String} title The title bar text
3705          * @param {String} msg The message box body text
3706          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3707          * @param {Object} scope (optional) The scope of the callback function
3708          * @return {Roo.MessageBox} This message box
3709          */
3710         confirm : function(title, msg, fn, scope){
3711             this.show({
3712                 title : title,
3713                 msg : msg,
3714                 buttons: this.YESNO,
3715                 fn: fn,
3716                 scope : scope,
3717                 modal : true
3718             });
3719             return this;
3720         },
3721
3722         /**
3723          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3724          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3725          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3726          * (could also be the top-right close button) and the text that was entered will be passed as the two
3727          * parameters to the callback.
3728          * @param {String} title The title bar text
3729          * @param {String} msg The message box body text
3730          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3731          * @param {Object} scope (optional) The scope of the callback function
3732          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3733          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3734          * @return {Roo.MessageBox} This message box
3735          */
3736         prompt : function(title, msg, fn, scope, multiline){
3737             this.show({
3738                 title : title,
3739                 msg : msg,
3740                 buttons: this.OKCANCEL,
3741                 fn: fn,
3742                 minWidth:250,
3743                 scope : scope,
3744                 prompt:true,
3745                 multiline: multiline,
3746                 modal : true
3747             });
3748             return this;
3749         },
3750
3751         /**
3752          * Button config that displays a single OK button
3753          * @type Object
3754          */
3755         OK : {ok:true},
3756         /**
3757          * Button config that displays Yes and No buttons
3758          * @type Object
3759          */
3760         YESNO : {yes:true, no:true},
3761         /**
3762          * Button config that displays OK and Cancel buttons
3763          * @type Object
3764          */
3765         OKCANCEL : {ok:true, cancel:true},
3766         /**
3767          * Button config that displays Yes, No and Cancel buttons
3768          * @type Object
3769          */
3770         YESNOCANCEL : {yes:true, no:true, cancel:true},
3771
3772         /**
3773          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3774          * @type Number
3775          */
3776         defaultTextHeight : 75,
3777         /**
3778          * The maximum width in pixels of the message box (defaults to 600)
3779          * @type Number
3780          */
3781         maxWidth : 600,
3782         /**
3783          * The minimum width in pixels of the message box (defaults to 100)
3784          * @type Number
3785          */
3786         minWidth : 100,
3787         /**
3788          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3789          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3790          * @type Number
3791          */
3792         minProgressWidth : 250,
3793         /**
3794          * An object containing the default button text strings that can be overriden for localized language support.
3795          * Supported properties are: ok, cancel, yes and no.
3796          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3797          * @type Object
3798          */
3799         buttonText : {
3800             ok : "OK",
3801             cancel : "Cancel",
3802             yes : "Yes",
3803             no : "No"
3804         }
3805     };
3806 }();
3807
3808 /**
3809  * Shorthand for {@link Roo.MessageBox}
3810  */
3811 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3812 Roo.Msg = Roo.Msg || Roo.MessageBox;
3813 /*
3814  * - LGPL
3815  *
3816  * navbar
3817  * 
3818  */
3819
3820 /**
3821  * @class Roo.bootstrap.Navbar
3822  * @extends Roo.bootstrap.Component
3823  * Bootstrap Navbar class
3824
3825  * @constructor
3826  * Create a new Navbar
3827  * @param {Object} config The config object
3828  */
3829
3830
3831 Roo.bootstrap.Navbar = function(config){
3832     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3833     this.addEvents({
3834         // raw events
3835         /**
3836          * @event beforetoggle
3837          * Fire before toggle the menu
3838          * @param {Roo.EventObject} e
3839          */
3840         "beforetoggle" : true
3841     });
3842 };
3843
3844 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3845     
3846     
3847    
3848     // private
3849     navItems : false,
3850     loadMask : false,
3851     
3852     
3853     getAutoCreate : function(){
3854         
3855         
3856         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3857         
3858     },
3859     
3860     initEvents :function ()
3861     {
3862         //Roo.log(this.el.select('.navbar-toggle',true));
3863         this.el.select('.navbar-toggle',true).on('click', function() {
3864             if(this.fireEvent('beforetoggle', this) !== false){
3865                 var ce = this.el.select('.navbar-collapse',true).first();
3866                 ce.toggleClass('in'); // old...
3867                 if (ce.hasClass('collapse')) {
3868                     // show it...
3869                     ce.removeClass('collapse');
3870                     ce.addClass('show');
3871                     var h = ce.getHeight();
3872                     Roo.log(h);
3873                     ce.removeClass('show');
3874                     // at this point we should be able to see it..
3875                     ce.addClass('collapsing');
3876                     
3877                     ce.setHeight(0); // resize it ...
3878                     ce.on('transitionend', function() {
3879                         Roo.log('done transition');
3880                         ce.removeClass('collapsing');
3881                         ce.addClass('show');
3882                         ce.removeClass('collapse');
3883
3884                         ce.dom.style.height = '';
3885                     }, this, { single: true} );
3886                     ce.setHeight(h);
3887                     
3888                 } else {
3889                     ce.setHeight(ce.getHeight());
3890                     ce.removeClass('show');
3891                     ce.addClass('collapsing');
3892                     
3893                     ce.on('transitionend', function() {
3894                         ce.dom.style.height = '';
3895                         ce.removeClass('collapsing');
3896                         ce.addClass('collapse');
3897                     }, this, { single: true} );
3898                     ce.setHeight(0);
3899                 }
3900             }
3901             
3902         }, this);
3903         
3904         var mark = {
3905             tag: "div",
3906             cls:"x-dlg-mask"
3907         };
3908         
3909         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3910         
3911         var size = this.el.getSize();
3912         this.maskEl.setSize(size.width, size.height);
3913         this.maskEl.enableDisplayMode("block");
3914         this.maskEl.hide();
3915         
3916         if(this.loadMask){
3917             this.maskEl.show();
3918         }
3919     },
3920     
3921     
3922     getChildContainer : function()
3923     {
3924         if (this.el.select('.collapse').getCount()) {
3925             return this.el.select('.collapse',true).first();
3926         }
3927         
3928         return this.el;
3929     },
3930     
3931     mask : function()
3932     {
3933         this.maskEl.show();
3934     },
3935     
3936     unmask : function()
3937     {
3938         this.maskEl.hide();
3939     } 
3940     
3941     
3942     
3943     
3944 });
3945
3946
3947
3948  
3949
3950  /*
3951  * - LGPL
3952  *
3953  * navbar
3954  * 
3955  */
3956
3957 /**
3958  * @class Roo.bootstrap.NavSimplebar
3959  * @extends Roo.bootstrap.Navbar
3960  * Bootstrap Sidebar class
3961  *
3962  * @cfg {Boolean} inverse is inverted color
3963  * 
3964  * @cfg {String} type (nav | pills | tabs)
3965  * @cfg {Boolean} arrangement stacked | justified
3966  * @cfg {String} align (left | right) alignment
3967  * 
3968  * @cfg {Boolean} main (true|false) main nav bar? default false
3969  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3970  * 
3971  * @cfg {String} tag (header|footer|nav|div) default is nav 
3972
3973  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3974  * 
3975  * 
3976  * @constructor
3977  * Create a new Sidebar
3978  * @param {Object} config The config object
3979  */
3980
3981
3982 Roo.bootstrap.NavSimplebar = function(config){
3983     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3984 };
3985
3986 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3987     
3988     inverse: false,
3989     
3990     type: false,
3991     arrangement: '',
3992     align : false,
3993     
3994     weight : 'light',
3995     
3996     main : false,
3997     
3998     
3999     tag : false,
4000     
4001     
4002     getAutoCreate : function(){
4003         
4004         
4005         var cfg = {
4006             tag : this.tag || 'div',
4007             cls : 'navbar navbar-expand-lg'
4008         };
4009         if (['light','white'].indexOf(this.weight) > -1) {
4010             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4011         }
4012         cfg.cls += ' bg-' + this.weight;
4013         
4014           
4015         
4016         cfg.cn = [
4017             {
4018                 cls: 'nav',
4019                 tag : 'ul'
4020             }
4021         ];
4022         
4023          
4024         this.type = this.type || 'nav';
4025         if (['tabs','pills'].indexOf(this.type)!==-1) {
4026             cfg.cn[0].cls += ' nav-' + this.type
4027         
4028         
4029         } else {
4030             if (this.type!=='nav') {
4031                 Roo.log('nav type must be nav/tabs/pills')
4032             }
4033             cfg.cn[0].cls += ' navbar-nav'
4034         }
4035         
4036         
4037         
4038         
4039         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4040             cfg.cn[0].cls += ' nav-' + this.arrangement;
4041         }
4042         
4043         
4044         if (this.align === 'right') {
4045             cfg.cn[0].cls += ' navbar-right';
4046         }
4047         
4048         if (this.inverse) {
4049             cfg.cls += ' navbar-inverse';
4050             
4051         }
4052         
4053         
4054         return cfg;
4055     
4056         
4057     }
4058     
4059     
4060     
4061 });
4062
4063
4064
4065  
4066
4067  
4068        /*
4069  * - LGPL
4070  *
4071  * navbar
4072  * navbar-fixed-top
4073  * navbar-expand-md  fixed-top 
4074  */
4075
4076 /**
4077  * @class Roo.bootstrap.NavHeaderbar
4078  * @extends Roo.bootstrap.NavSimplebar
4079  * Bootstrap Sidebar class
4080  *
4081  * @cfg {String} brand what is brand
4082  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4083  * @cfg {String} brand_href href of the brand
4084  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4085  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4086  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4087  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4088  * 
4089  * @constructor
4090  * Create a new Sidebar
4091  * @param {Object} config The config object
4092  */
4093
4094
4095 Roo.bootstrap.NavHeaderbar = function(config){
4096     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4097       
4098 };
4099
4100 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4101     
4102     position: '',
4103     brand: '',
4104     brand_href: false,
4105     srButton : true,
4106     autohide : false,
4107     desktopCenter : false,
4108    
4109     
4110     getAutoCreate : function(){
4111         
4112         var   cfg = {
4113             tag: this.nav || 'nav',
4114             cls: 'navbar navbar-expand-md',
4115             role: 'navigation',
4116             cn: []
4117         };
4118         
4119         var cn = cfg.cn;
4120         if (this.desktopCenter) {
4121             cn.push({cls : 'container', cn : []});
4122             cn = cn[0].cn;
4123         }
4124         
4125         if(this.srButton){
4126             var btn = {
4127                 tag: 'button',
4128                 type: 'button',
4129                 cls: 'navbar-toggle navbar-toggler',
4130                 'data-toggle': 'collapse',
4131                 cn: [
4132                     {
4133                         tag: 'span',
4134                         cls: 'sr-only',
4135                         html: 'Toggle navigation'
4136                     },
4137                     {
4138                         tag: 'span',
4139                         cls: 'icon-bar navbar-toggler-icon'
4140                     },
4141                     {
4142                         tag: 'span',
4143                         cls: 'icon-bar'
4144                     },
4145                     {
4146                         tag: 'span',
4147                         cls: 'icon-bar'
4148                     }
4149                 ]
4150             };
4151             
4152             cn.push( Roo.bootstrap.version == 4 ? btn : {
4153                 tag: 'div',
4154                 cls: 'navbar-header',
4155                 cn: [
4156                     btn
4157                 ]
4158             });
4159         }
4160         
4161         cn.push({
4162             tag: 'div',
4163             cls: 'collapse navbar-collapse',
4164             cn : []
4165         });
4166         
4167         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4168         
4169         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4170             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4171             
4172             // tag can override this..
4173             
4174             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4175         }
4176         
4177         if (this.brand !== '') {
4178             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4179             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4180                 tag: 'a',
4181                 href: this.brand_href ? this.brand_href : '#',
4182                 cls: 'navbar-brand',
4183                 cn: [
4184                 this.brand
4185                 ]
4186             });
4187         }
4188         
4189         if(this.main){
4190             cfg.cls += ' main-nav';
4191         }
4192         
4193         
4194         return cfg;
4195
4196         
4197     },
4198     getHeaderChildContainer : function()
4199     {
4200         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4201             return this.el.select('.navbar-header',true).first();
4202         }
4203         
4204         return this.getChildContainer();
4205     },
4206     
4207     
4208     initEvents : function()
4209     {
4210         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4211         
4212         if (this.autohide) {
4213             
4214             var prevScroll = 0;
4215             var ft = this.el;
4216             
4217             Roo.get(document).on('scroll',function(e) {
4218                 var ns = Roo.get(document).getScroll().top;
4219                 var os = prevScroll;
4220                 prevScroll = ns;
4221                 
4222                 if(ns > os){
4223                     ft.removeClass('slideDown');
4224                     ft.addClass('slideUp');
4225                     return;
4226                 }
4227                 ft.removeClass('slideUp');
4228                 ft.addClass('slideDown');
4229                  
4230               
4231           },this);
4232         }
4233     }    
4234     
4235 });
4236
4237
4238
4239  
4240
4241  /*
4242  * - LGPL
4243  *
4244  * navbar
4245  * 
4246  */
4247
4248 /**
4249  * @class Roo.bootstrap.NavSidebar
4250  * @extends Roo.bootstrap.Navbar
4251  * Bootstrap Sidebar class
4252  * 
4253  * @constructor
4254  * Create a new Sidebar
4255  * @param {Object} config The config object
4256  */
4257
4258
4259 Roo.bootstrap.NavSidebar = function(config){
4260     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4261 };
4262
4263 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4264     
4265     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4266     
4267     getAutoCreate : function(){
4268         
4269         
4270         return  {
4271             tag: 'div',
4272             cls: 'sidebar sidebar-nav'
4273         };
4274     
4275         
4276     }
4277     
4278     
4279     
4280 });
4281
4282
4283
4284  
4285
4286  /*
4287  * - LGPL
4288  *
4289  * nav group
4290  * 
4291  */
4292
4293 /**
4294  * @class Roo.bootstrap.NavGroup
4295  * @extends Roo.bootstrap.Component
4296  * Bootstrap NavGroup class
4297  * @cfg {String} align (left|right)
4298  * @cfg {Boolean} inverse
4299  * @cfg {String} type (nav|pills|tab) default nav
4300  * @cfg {String} navId - reference Id for navbar.
4301
4302  * 
4303  * @constructor
4304  * Create a new nav group
4305  * @param {Object} config The config object
4306  */
4307
4308 Roo.bootstrap.NavGroup = function(config){
4309     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4310     this.navItems = [];
4311    
4312     Roo.bootstrap.NavGroup.register(this);
4313      this.addEvents({
4314         /**
4315              * @event changed
4316              * Fires when the active item changes
4317              * @param {Roo.bootstrap.NavGroup} this
4318              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4319              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4320          */
4321         'changed': true
4322      });
4323     
4324 };
4325
4326 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4327     
4328     align: '',
4329     inverse: false,
4330     form: false,
4331     type: 'nav',
4332     navId : '',
4333     // private
4334     
4335     navItems : false, 
4336     
4337     getAutoCreate : function()
4338     {
4339         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4340         
4341         cfg = {
4342             tag : 'ul',
4343             cls: 'nav' 
4344         };
4345         
4346         if (['tabs','pills'].indexOf(this.type)!==-1) {
4347             cfg.cls += ' nav-' + this.type
4348         } else {
4349             if (this.type!=='nav') {
4350                 Roo.log('nav type must be nav/tabs/pills')
4351             }
4352             cfg.cls += ' navbar-nav'
4353         }
4354         
4355         if (this.parent() && this.parent().sidebar) {
4356             cfg = {
4357                 tag: 'ul',
4358                 cls: 'dashboard-menu sidebar-menu'
4359             };
4360             
4361             return cfg;
4362         }
4363         
4364         if (this.form === true) {
4365             cfg = {
4366                 tag: 'form',
4367                 cls: 'navbar-form'
4368             };
4369             
4370             if (this.align === 'right') {
4371                 cfg.cls += ' navbar-right ml-md-auto';
4372             } else {
4373                 cfg.cls += ' navbar-left';
4374             }
4375         }
4376         
4377         if (this.align === 'right') {
4378             cfg.cls += ' navbar-right ml-md-auto';
4379         } else {
4380             cfg.cls += ' mr-auto';
4381         }
4382         
4383         if (this.inverse) {
4384             cfg.cls += ' navbar-inverse';
4385             
4386         }
4387         
4388         
4389         return cfg;
4390     },
4391     /**
4392     * sets the active Navigation item
4393     * @param {Roo.bootstrap.NavItem} the new current navitem
4394     */
4395     setActiveItem : function(item)
4396     {
4397         var prev = false;
4398         Roo.each(this.navItems, function(v){
4399             if (v == item) {
4400                 return ;
4401             }
4402             if (v.isActive()) {
4403                 v.setActive(false, true);
4404                 prev = v;
4405                 
4406             }
4407             
4408         });
4409
4410         item.setActive(true, true);
4411         this.fireEvent('changed', this, item, prev);
4412         
4413         
4414     },
4415     /**
4416     * gets the active Navigation item
4417     * @return {Roo.bootstrap.NavItem} the current navitem
4418     */
4419     getActive : function()
4420     {
4421         
4422         var prev = false;
4423         Roo.each(this.navItems, function(v){
4424             
4425             if (v.isActive()) {
4426                 prev = v;
4427                 
4428             }
4429             
4430         });
4431         return prev;
4432     },
4433     
4434     indexOfNav : function()
4435     {
4436         
4437         var prev = false;
4438         Roo.each(this.navItems, function(v,i){
4439             
4440             if (v.isActive()) {
4441                 prev = i;
4442                 
4443             }
4444             
4445         });
4446         return prev;
4447     },
4448     /**
4449     * adds a Navigation item
4450     * @param {Roo.bootstrap.NavItem} the navitem to add
4451     */
4452     addItem : function(cfg)
4453     {
4454         var cn = new Roo.bootstrap.NavItem(cfg);
4455         this.register(cn);
4456         cn.parentId = this.id;
4457         cn.onRender(this.el, null);
4458         return cn;
4459     },
4460     /**
4461     * register a Navigation item
4462     * @param {Roo.bootstrap.NavItem} the navitem to add
4463     */
4464     register : function(item)
4465     {
4466         this.navItems.push( item);
4467         item.navId = this.navId;
4468     
4469     },
4470     
4471     /**
4472     * clear all the Navigation item
4473     */
4474    
4475     clearAll : function()
4476     {
4477         this.navItems = [];
4478         this.el.dom.innerHTML = '';
4479     },
4480     
4481     getNavItem: function(tabId)
4482     {
4483         var ret = false;
4484         Roo.each(this.navItems, function(e) {
4485             if (e.tabId == tabId) {
4486                ret =  e;
4487                return false;
4488             }
4489             return true;
4490             
4491         });
4492         return ret;
4493     },
4494     
4495     setActiveNext : function()
4496     {
4497         var i = this.indexOfNav(this.getActive());
4498         if (i > this.navItems.length) {
4499             return;
4500         }
4501         this.setActiveItem(this.navItems[i+1]);
4502     },
4503     setActivePrev : function()
4504     {
4505         var i = this.indexOfNav(this.getActive());
4506         if (i  < 1) {
4507             return;
4508         }
4509         this.setActiveItem(this.navItems[i-1]);
4510     },
4511     clearWasActive : function(except) {
4512         Roo.each(this.navItems, function(e) {
4513             if (e.tabId != except.tabId && e.was_active) {
4514                e.was_active = false;
4515                return false;
4516             }
4517             return true;
4518             
4519         });
4520     },
4521     getWasActive : function ()
4522     {
4523         var r = false;
4524         Roo.each(this.navItems, function(e) {
4525             if (e.was_active) {
4526                r = e;
4527                return false;
4528             }
4529             return true;
4530             
4531         });
4532         return r;
4533     }
4534     
4535     
4536 });
4537
4538  
4539 Roo.apply(Roo.bootstrap.NavGroup, {
4540     
4541     groups: {},
4542      /**
4543     * register a Navigation Group
4544     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4545     */
4546     register : function(navgrp)
4547     {
4548         this.groups[navgrp.navId] = navgrp;
4549         
4550     },
4551     /**
4552     * fetch a Navigation Group based on the navigation ID
4553     * @param {string} the navgroup to add
4554     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4555     */
4556     get: function(navId) {
4557         if (typeof(this.groups[navId]) == 'undefined') {
4558             return false;
4559             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4560         }
4561         return this.groups[navId] ;
4562     }
4563     
4564     
4565     
4566 });
4567
4568  /*
4569  * - LGPL
4570  *
4571  * row
4572  * 
4573  */
4574
4575 /**
4576  * @class Roo.bootstrap.NavItem
4577  * @extends Roo.bootstrap.Component
4578  * Bootstrap Navbar.NavItem class
4579  * @cfg {String} href  link to
4580  * @cfg {String} html content of button
4581  * @cfg {String} badge text inside badge
4582  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4583  * @cfg {String} glyphicon DEPRICATED - use fa
4584  * @cfg {String} icon DEPRICATED - use fa
4585  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4586  * @cfg {Boolean} active Is item active
4587  * @cfg {Boolean} disabled Is item disabled
4588  
4589  * @cfg {Boolean} preventDefault (true | false) default false
4590  * @cfg {String} tabId the tab that this item activates.
4591  * @cfg {String} tagtype (a|span) render as a href or span?
4592  * @cfg {Boolean} animateRef (true|false) link to element default false  
4593   
4594  * @constructor
4595  * Create a new Navbar Item
4596  * @param {Object} config The config object
4597  */
4598 Roo.bootstrap.NavItem = function(config){
4599     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4600     this.addEvents({
4601         // raw events
4602         /**
4603          * @event click
4604          * The raw click event for the entire grid.
4605          * @param {Roo.EventObject} e
4606          */
4607         "click" : true,
4608          /**
4609             * @event changed
4610             * Fires when the active item active state changes
4611             * @param {Roo.bootstrap.NavItem} this
4612             * @param {boolean} state the new state
4613              
4614          */
4615         'changed': true,
4616         /**
4617             * @event scrollto
4618             * Fires when scroll to element
4619             * @param {Roo.bootstrap.NavItem} this
4620             * @param {Object} options
4621             * @param {Roo.EventObject} e
4622              
4623          */
4624         'scrollto': true
4625     });
4626    
4627 };
4628
4629 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4630     
4631     href: false,
4632     html: '',
4633     badge: '',
4634     icon: false,
4635     fa : false,
4636     glyphicon: false,
4637     active: false,
4638     preventDefault : false,
4639     tabId : false,
4640     tagtype : 'a',
4641     disabled : false,
4642     animateRef : false,
4643     was_active : false,
4644     
4645     getAutoCreate : function(){
4646          
4647         var cfg = {
4648             tag: 'li',
4649             cls: 'nav-item'
4650             
4651         };
4652         
4653         if (this.active) {
4654             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4655         }
4656         if (this.disabled) {
4657             cfg.cls += ' disabled';
4658         }
4659         
4660         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4661             cfg.cn = [
4662                 {
4663                     tag: this.tagtype,
4664                     href : this.href || "#",
4665                     html: this.html || ''
4666                 }
4667             ];
4668             if (this.tagtype == 'a') {
4669                 cfg.cn[0].cls = 'nav-link';
4670             }
4671             if (this.icon) {
4672                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4673             }
4674             if (this.fa) {
4675                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4676             }
4677             if(this.glyphicon) {
4678                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4679             }
4680             
4681             if (this.menu) {
4682                 
4683                 cfg.cn[0].html += " <span class='caret'></span>";
4684              
4685             }
4686             
4687             if (this.badge !== '') {
4688                  
4689                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4690             }
4691         }
4692         
4693         
4694         
4695         return cfg;
4696     },
4697     initEvents: function() 
4698     {
4699         if (typeof (this.menu) != 'undefined') {
4700             this.menu.parentType = this.xtype;
4701             this.menu.triggerEl = this.el;
4702             this.menu = this.addxtype(Roo.apply({}, this.menu));
4703         }
4704         
4705         this.el.select('a',true).on('click', this.onClick, this);
4706         
4707         if(this.tagtype == 'span'){
4708             this.el.select('span',true).on('click', this.onClick, this);
4709         }
4710        
4711         // at this point parent should be available..
4712         this.parent().register(this);
4713     },
4714     
4715     onClick : function(e)
4716     {
4717         if (e.getTarget('.dropdown-menu-item')) {
4718             // did you click on a menu itemm.... - then don't trigger onclick..
4719             return;
4720         }
4721         
4722         if(
4723                 this.preventDefault || 
4724                 this.href == '#' 
4725         ){
4726             Roo.log("NavItem - prevent Default?");
4727             e.preventDefault();
4728         }
4729         
4730         if (this.disabled) {
4731             return;
4732         }
4733         
4734         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4735         if (tg && tg.transition) {
4736             Roo.log("waiting for the transitionend");
4737             return;
4738         }
4739         
4740         
4741         
4742         //Roo.log("fire event clicked");
4743         if(this.fireEvent('click', this, e) === false){
4744             return;
4745         };
4746         
4747         if(this.tagtype == 'span'){
4748             return;
4749         }
4750         
4751         //Roo.log(this.href);
4752         var ael = this.el.select('a',true).first();
4753         //Roo.log(ael);
4754         
4755         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4756             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4757             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4758                 return; // ignore... - it's a 'hash' to another page.
4759             }
4760             Roo.log("NavItem - prevent Default?");
4761             e.preventDefault();
4762             this.scrollToElement(e);
4763         }
4764         
4765         
4766         var p =  this.parent();
4767    
4768         if (['tabs','pills'].indexOf(p.type)!==-1) {
4769             if (typeof(p.setActiveItem) !== 'undefined') {
4770                 p.setActiveItem(this);
4771             }
4772         }
4773         
4774         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4775         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4776             // remove the collapsed menu expand...
4777             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4778         }
4779     },
4780     
4781     isActive: function () {
4782         return this.active
4783     },
4784     setActive : function(state, fire, is_was_active)
4785     {
4786         if (this.active && !state && this.navId) {
4787             this.was_active = true;
4788             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4789             if (nv) {
4790                 nv.clearWasActive(this);
4791             }
4792             
4793         }
4794         this.active = state;
4795         
4796         if (!state ) {
4797             this.el.removeClass('active');
4798         } else if (!this.el.hasClass('active')) {
4799             this.el.addClass('active');
4800         }
4801         if (fire) {
4802             this.fireEvent('changed', this, state);
4803         }
4804         
4805         // show a panel if it's registered and related..
4806         
4807         if (!this.navId || !this.tabId || !state || is_was_active) {
4808             return;
4809         }
4810         
4811         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4812         if (!tg) {
4813             return;
4814         }
4815         var pan = tg.getPanelByName(this.tabId);
4816         if (!pan) {
4817             return;
4818         }
4819         // if we can not flip to new panel - go back to old nav highlight..
4820         if (false == tg.showPanel(pan)) {
4821             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4822             if (nv) {
4823                 var onav = nv.getWasActive();
4824                 if (onav) {
4825                     onav.setActive(true, false, true);
4826                 }
4827             }
4828             
4829         }
4830         
4831         
4832         
4833     },
4834      // this should not be here...
4835     setDisabled : function(state)
4836     {
4837         this.disabled = state;
4838         if (!state ) {
4839             this.el.removeClass('disabled');
4840         } else if (!this.el.hasClass('disabled')) {
4841             this.el.addClass('disabled');
4842         }
4843         
4844     },
4845     
4846     /**
4847      * Fetch the element to display the tooltip on.
4848      * @return {Roo.Element} defaults to this.el
4849      */
4850     tooltipEl : function()
4851     {
4852         return this.el.select('' + this.tagtype + '', true).first();
4853     },
4854     
4855     scrollToElement : function(e)
4856     {
4857         var c = document.body;
4858         
4859         /*
4860          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4861          */
4862         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4863             c = document.documentElement;
4864         }
4865         
4866         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4867         
4868         if(!target){
4869             return;
4870         }
4871
4872         var o = target.calcOffsetsTo(c);
4873         
4874         var options = {
4875             target : target,
4876             value : o[1]
4877         };
4878         
4879         this.fireEvent('scrollto', this, options, e);
4880         
4881         Roo.get(c).scrollTo('top', options.value, true);
4882         
4883         return;
4884     }
4885 });
4886  
4887
4888  /*
4889  * - LGPL
4890  *
4891  * sidebar item
4892  *
4893  *  li
4894  *    <span> icon </span>
4895  *    <span> text </span>
4896  *    <span>badge </span>
4897  */
4898
4899 /**
4900  * @class Roo.bootstrap.NavSidebarItem
4901  * @extends Roo.bootstrap.NavItem
4902  * Bootstrap Navbar.NavSidebarItem class
4903  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4904  * {Boolean} open is the menu open
4905  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4906  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4907  * {String} buttonSize (sm|md|lg)the extra classes for the button
4908  * {Boolean} showArrow show arrow next to the text (default true)
4909  * @constructor
4910  * Create a new Navbar Button
4911  * @param {Object} config The config object
4912  */
4913 Roo.bootstrap.NavSidebarItem = function(config){
4914     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4915     this.addEvents({
4916         // raw events
4917         /**
4918          * @event click
4919          * The raw click event for the entire grid.
4920          * @param {Roo.EventObject} e
4921          */
4922         "click" : true,
4923          /**
4924             * @event changed
4925             * Fires when the active item active state changes
4926             * @param {Roo.bootstrap.NavSidebarItem} this
4927             * @param {boolean} state the new state
4928              
4929          */
4930         'changed': true
4931     });
4932    
4933 };
4934
4935 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4936     
4937     badgeWeight : 'default',
4938     
4939     open: false,
4940     
4941     buttonView : false,
4942     
4943     buttonWeight : 'default',
4944     
4945     buttonSize : 'md',
4946     
4947     showArrow : true,
4948     
4949     getAutoCreate : function(){
4950         
4951         
4952         var a = {
4953                 tag: 'a',
4954                 href : this.href || '#',
4955                 cls: '',
4956                 html : '',
4957                 cn : []
4958         };
4959         
4960         if(this.buttonView){
4961             a = {
4962                 tag: 'button',
4963                 href : this.href || '#',
4964                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4965                 html : this.html,
4966                 cn : []
4967             };
4968         }
4969         
4970         var cfg = {
4971             tag: 'li',
4972             cls: '',
4973             cn: [ a ]
4974         };
4975         
4976         if (this.active) {
4977             cfg.cls += ' active';
4978         }
4979         
4980         if (this.disabled) {
4981             cfg.cls += ' disabled';
4982         }
4983         if (this.open) {
4984             cfg.cls += ' open x-open';
4985         }
4986         // left icon..
4987         if (this.glyphicon || this.icon) {
4988             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4989             a.cn.push({ tag : 'i', cls : c }) ;
4990         }
4991         
4992         if(!this.buttonView){
4993             var span = {
4994                 tag: 'span',
4995                 html : this.html || ''
4996             };
4997
4998             a.cn.push(span);
4999             
5000         }
5001         
5002         if (this.badge !== '') {
5003             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5004         }
5005         
5006         if (this.menu) {
5007             
5008             if(this.showArrow){
5009                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5010             }
5011             
5012             a.cls += ' dropdown-toggle treeview' ;
5013         }
5014         
5015         return cfg;
5016     },
5017     
5018     initEvents : function()
5019     { 
5020         if (typeof (this.menu) != 'undefined') {
5021             this.menu.parentType = this.xtype;
5022             this.menu.triggerEl = this.el;
5023             this.menu = this.addxtype(Roo.apply({}, this.menu));
5024         }
5025         
5026         this.el.on('click', this.onClick, this);
5027         
5028         if(this.badge !== ''){
5029             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5030         }
5031         
5032     },
5033     
5034     onClick : function(e)
5035     {
5036         if(this.disabled){
5037             e.preventDefault();
5038             return;
5039         }
5040         
5041         if(this.preventDefault){
5042             e.preventDefault();
5043         }
5044         
5045         this.fireEvent('click', this);
5046     },
5047     
5048     disable : function()
5049     {
5050         this.setDisabled(true);
5051     },
5052     
5053     enable : function()
5054     {
5055         this.setDisabled(false);
5056     },
5057     
5058     setDisabled : function(state)
5059     {
5060         if(this.disabled == state){
5061             return;
5062         }
5063         
5064         this.disabled = state;
5065         
5066         if (state) {
5067             this.el.addClass('disabled');
5068             return;
5069         }
5070         
5071         this.el.removeClass('disabled');
5072         
5073         return;
5074     },
5075     
5076     setActive : function(state)
5077     {
5078         if(this.active == state){
5079             return;
5080         }
5081         
5082         this.active = state;
5083         
5084         if (state) {
5085             this.el.addClass('active');
5086             return;
5087         }
5088         
5089         this.el.removeClass('active');
5090         
5091         return;
5092     },
5093     
5094     isActive: function () 
5095     {
5096         return this.active;
5097     },
5098     
5099     setBadge : function(str)
5100     {
5101         if(!this.badgeEl){
5102             return;
5103         }
5104         
5105         this.badgeEl.dom.innerHTML = str;
5106     }
5107     
5108    
5109      
5110  
5111 });
5112  
5113
5114  /*
5115  * - LGPL
5116  *
5117  * row
5118  * 
5119  */
5120
5121 /**
5122  * @class Roo.bootstrap.Row
5123  * @extends Roo.bootstrap.Component
5124  * Bootstrap Row class (contains columns...)
5125  * 
5126  * @constructor
5127  * Create a new Row
5128  * @param {Object} config The config object
5129  */
5130
5131 Roo.bootstrap.Row = function(config){
5132     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5133 };
5134
5135 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5136     
5137     getAutoCreate : function(){
5138        return {
5139             cls: 'row clearfix'
5140        };
5141     }
5142     
5143     
5144 });
5145
5146  
5147
5148  /*
5149  * - LGPL
5150  *
5151  * element
5152  * 
5153  */
5154
5155 /**
5156  * @class Roo.bootstrap.Element
5157  * @extends Roo.bootstrap.Component
5158  * Bootstrap Element class
5159  * @cfg {String} html contents of the element
5160  * @cfg {String} tag tag of the element
5161  * @cfg {String} cls class of the element
5162  * @cfg {Boolean} preventDefault (true|false) default false
5163  * @cfg {Boolean} clickable (true|false) default false
5164  * 
5165  * @constructor
5166  * Create a new Element
5167  * @param {Object} config The config object
5168  */
5169
5170 Roo.bootstrap.Element = function(config){
5171     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5172     
5173     this.addEvents({
5174         // raw events
5175         /**
5176          * @event click
5177          * When a element is chick
5178          * @param {Roo.bootstrap.Element} this
5179          * @param {Roo.EventObject} e
5180          */
5181         "click" : true
5182     });
5183 };
5184
5185 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5186     
5187     tag: 'div',
5188     cls: '',
5189     html: '',
5190     preventDefault: false, 
5191     clickable: false,
5192     
5193     getAutoCreate : function(){
5194         
5195         var cfg = {
5196             tag: this.tag,
5197             // cls: this.cls, double assign in parent class Component.js :: onRender
5198             html: this.html
5199         };
5200         
5201         return cfg;
5202     },
5203     
5204     initEvents: function() 
5205     {
5206         Roo.bootstrap.Element.superclass.initEvents.call(this);
5207         
5208         if(this.clickable){
5209             this.el.on('click', this.onClick, this);
5210         }
5211         
5212     },
5213     
5214     onClick : function(e)
5215     {
5216         if(this.preventDefault){
5217             e.preventDefault();
5218         }
5219         
5220         this.fireEvent('click', this, e);
5221     },
5222     
5223     getValue : function()
5224     {
5225         return this.el.dom.innerHTML;
5226     },
5227     
5228     setValue : function(value)
5229     {
5230         this.el.dom.innerHTML = value;
5231     }
5232    
5233 });
5234
5235  
5236
5237  /*
5238  * - LGPL
5239  *
5240  * pagination
5241  * 
5242  */
5243
5244 /**
5245  * @class Roo.bootstrap.Pagination
5246  * @extends Roo.bootstrap.Component
5247  * Bootstrap Pagination class
5248  * @cfg {String} size xs | sm | md | lg
5249  * @cfg {Boolean} inverse false | true
5250  * 
5251  * @constructor
5252  * Create a new Pagination
5253  * @param {Object} config The config object
5254  */
5255
5256 Roo.bootstrap.Pagination = function(config){
5257     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5258 };
5259
5260 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5261     
5262     cls: false,
5263     size: false,
5264     inverse: false,
5265     
5266     getAutoCreate : function(){
5267         var cfg = {
5268             tag: 'ul',
5269                 cls: 'pagination'
5270         };
5271         if (this.inverse) {
5272             cfg.cls += ' inverse';
5273         }
5274         if (this.html) {
5275             cfg.html=this.html;
5276         }
5277         if (this.cls) {
5278             cfg.cls += " " + this.cls;
5279         }
5280         return cfg;
5281     }
5282    
5283 });
5284
5285  
5286
5287  /*
5288  * - LGPL
5289  *
5290  * Pagination item
5291  * 
5292  */
5293
5294
5295 /**
5296  * @class Roo.bootstrap.PaginationItem
5297  * @extends Roo.bootstrap.Component
5298  * Bootstrap PaginationItem class
5299  * @cfg {String} html text
5300  * @cfg {String} href the link
5301  * @cfg {Boolean} preventDefault (true | false) default true
5302  * @cfg {Boolean} active (true | false) default false
5303  * @cfg {Boolean} disabled default false
5304  * 
5305  * 
5306  * @constructor
5307  * Create a new PaginationItem
5308  * @param {Object} config The config object
5309  */
5310
5311
5312 Roo.bootstrap.PaginationItem = function(config){
5313     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5314     this.addEvents({
5315         // raw events
5316         /**
5317          * @event click
5318          * The raw click event for the entire grid.
5319          * @param {Roo.EventObject} e
5320          */
5321         "click" : true
5322     });
5323 };
5324
5325 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5326     
5327     href : false,
5328     html : false,
5329     preventDefault: true,
5330     active : false,
5331     cls : false,
5332     disabled: false,
5333     
5334     getAutoCreate : function(){
5335         var cfg= {
5336             tag: 'li',
5337             cn: [
5338                 {
5339                     tag : 'a',
5340                     href : this.href ? this.href : '#',
5341                     html : this.html ? this.html : ''
5342                 }
5343             ]
5344         };
5345         
5346         if(this.cls){
5347             cfg.cls = this.cls;
5348         }
5349         
5350         if(this.disabled){
5351             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5352         }
5353         
5354         if(this.active){
5355             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5356         }
5357         
5358         return cfg;
5359     },
5360     
5361     initEvents: function() {
5362         
5363         this.el.on('click', this.onClick, this);
5364         
5365     },
5366     onClick : function(e)
5367     {
5368         Roo.log('PaginationItem on click ');
5369         if(this.preventDefault){
5370             e.preventDefault();
5371         }
5372         
5373         if(this.disabled){
5374             return;
5375         }
5376         
5377         this.fireEvent('click', this, e);
5378     }
5379    
5380 });
5381
5382  
5383
5384  /*
5385  * - LGPL
5386  *
5387  * slider
5388  * 
5389  */
5390
5391
5392 /**
5393  * @class Roo.bootstrap.Slider
5394  * @extends Roo.bootstrap.Component
5395  * Bootstrap Slider class
5396  *    
5397  * @constructor
5398  * Create a new Slider
5399  * @param {Object} config The config object
5400  */
5401
5402 Roo.bootstrap.Slider = function(config){
5403     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5404 };
5405
5406 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5407     
5408     getAutoCreate : function(){
5409         
5410         var cfg = {
5411             tag: 'div',
5412             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5413             cn: [
5414                 {
5415                     tag: 'a',
5416                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5417                 }
5418             ]
5419         };
5420         
5421         return cfg;
5422     }
5423    
5424 });
5425
5426  /*
5427  * Based on:
5428  * Ext JS Library 1.1.1
5429  * Copyright(c) 2006-2007, Ext JS, LLC.
5430  *
5431  * Originally Released Under LGPL - original licence link has changed is not relivant.
5432  *
5433  * Fork - LGPL
5434  * <script type="text/javascript">
5435  */
5436  
5437
5438 /**
5439  * @class Roo.grid.ColumnModel
5440  * @extends Roo.util.Observable
5441  * This is the default implementation of a ColumnModel used by the Grid. It defines
5442  * the columns in the grid.
5443  * <br>Usage:<br>
5444  <pre><code>
5445  var colModel = new Roo.grid.ColumnModel([
5446         {header: "Ticker", width: 60, sortable: true, locked: true},
5447         {header: "Company Name", width: 150, sortable: true},
5448         {header: "Market Cap.", width: 100, sortable: true},
5449         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5450         {header: "Employees", width: 100, sortable: true, resizable: false}
5451  ]);
5452  </code></pre>
5453  * <p>
5454  
5455  * The config options listed for this class are options which may appear in each
5456  * individual column definition.
5457  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5458  * @constructor
5459  * @param {Object} config An Array of column config objects. See this class's
5460  * config objects for details.
5461 */
5462 Roo.grid.ColumnModel = function(config){
5463         /**
5464      * The config passed into the constructor
5465      */
5466     this.config = config;
5467     this.lookup = {};
5468
5469     // if no id, create one
5470     // if the column does not have a dataIndex mapping,
5471     // map it to the order it is in the config
5472     for(var i = 0, len = config.length; i < len; i++){
5473         var c = config[i];
5474         if(typeof c.dataIndex == "undefined"){
5475             c.dataIndex = i;
5476         }
5477         if(typeof c.renderer == "string"){
5478             c.renderer = Roo.util.Format[c.renderer];
5479         }
5480         if(typeof c.id == "undefined"){
5481             c.id = Roo.id();
5482         }
5483         if(c.editor && c.editor.xtype){
5484             c.editor  = Roo.factory(c.editor, Roo.grid);
5485         }
5486         if(c.editor && c.editor.isFormField){
5487             c.editor = new Roo.grid.GridEditor(c.editor);
5488         }
5489         this.lookup[c.id] = c;
5490     }
5491
5492     /**
5493      * The width of columns which have no width specified (defaults to 100)
5494      * @type Number
5495      */
5496     this.defaultWidth = 100;
5497
5498     /**
5499      * Default sortable of columns which have no sortable specified (defaults to false)
5500      * @type Boolean
5501      */
5502     this.defaultSortable = false;
5503
5504     this.addEvents({
5505         /**
5506              * @event widthchange
5507              * Fires when the width of a column changes.
5508              * @param {ColumnModel} this
5509              * @param {Number} columnIndex The column index
5510              * @param {Number} newWidth The new width
5511              */
5512             "widthchange": true,
5513         /**
5514              * @event headerchange
5515              * Fires when the text of a header changes.
5516              * @param {ColumnModel} this
5517              * @param {Number} columnIndex The column index
5518              * @param {Number} newText The new header text
5519              */
5520             "headerchange": true,
5521         /**
5522              * @event hiddenchange
5523              * Fires when a column is hidden or "unhidden".
5524              * @param {ColumnModel} this
5525              * @param {Number} columnIndex The column index
5526              * @param {Boolean} hidden true if hidden, false otherwise
5527              */
5528             "hiddenchange": true,
5529             /**
5530          * @event columnmoved
5531          * Fires when a column is moved.
5532          * @param {ColumnModel} this
5533          * @param {Number} oldIndex
5534          * @param {Number} newIndex
5535          */
5536         "columnmoved" : true,
5537         /**
5538          * @event columlockchange
5539          * Fires when a column's locked state is changed
5540          * @param {ColumnModel} this
5541          * @param {Number} colIndex
5542          * @param {Boolean} locked true if locked
5543          */
5544         "columnlockchange" : true
5545     });
5546     Roo.grid.ColumnModel.superclass.constructor.call(this);
5547 };
5548 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5549     /**
5550      * @cfg {String} header The header text to display in the Grid view.
5551      */
5552     /**
5553      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5554      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5555      * specified, the column's index is used as an index into the Record's data Array.
5556      */
5557     /**
5558      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5559      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5560      */
5561     /**
5562      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5563      * Defaults to the value of the {@link #defaultSortable} property.
5564      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5565      */
5566     /**
5567      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5568      */
5569     /**
5570      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5571      */
5572     /**
5573      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5574      */
5575     /**
5576      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5577      */
5578     /**
5579      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5580      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5581      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5582      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5583      */
5584        /**
5585      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5586      */
5587     /**
5588      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5589      */
5590     /**
5591      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5592      */
5593     /**
5594      * @cfg {String} cursor (Optional)
5595      */
5596     /**
5597      * @cfg {String} tooltip (Optional)
5598      */
5599     /**
5600      * @cfg {Number} xs (Optional)
5601      */
5602     /**
5603      * @cfg {Number} sm (Optional)
5604      */
5605     /**
5606      * @cfg {Number} md (Optional)
5607      */
5608     /**
5609      * @cfg {Number} lg (Optional)
5610      */
5611     /**
5612      * Returns the id of the column at the specified index.
5613      * @param {Number} index The column index
5614      * @return {String} the id
5615      */
5616     getColumnId : function(index){
5617         return this.config[index].id;
5618     },
5619
5620     /**
5621      * Returns the column for a specified id.
5622      * @param {String} id The column id
5623      * @return {Object} the column
5624      */
5625     getColumnById : function(id){
5626         return this.lookup[id];
5627     },
5628
5629     
5630     /**
5631      * Returns the column for a specified dataIndex.
5632      * @param {String} dataIndex The column dataIndex
5633      * @return {Object|Boolean} the column or false if not found
5634      */
5635     getColumnByDataIndex: function(dataIndex){
5636         var index = this.findColumnIndex(dataIndex);
5637         return index > -1 ? this.config[index] : false;
5638     },
5639     
5640     /**
5641      * Returns the index for a specified column id.
5642      * @param {String} id The column id
5643      * @return {Number} the index, or -1 if not found
5644      */
5645     getIndexById : function(id){
5646         for(var i = 0, len = this.config.length; i < len; i++){
5647             if(this.config[i].id == id){
5648                 return i;
5649             }
5650         }
5651         return -1;
5652     },
5653     
5654     /**
5655      * Returns the index for a specified column dataIndex.
5656      * @param {String} dataIndex The column dataIndex
5657      * @return {Number} the index, or -1 if not found
5658      */
5659     
5660     findColumnIndex : function(dataIndex){
5661         for(var i = 0, len = this.config.length; i < len; i++){
5662             if(this.config[i].dataIndex == dataIndex){
5663                 return i;
5664             }
5665         }
5666         return -1;
5667     },
5668     
5669     
5670     moveColumn : function(oldIndex, newIndex){
5671         var c = this.config[oldIndex];
5672         this.config.splice(oldIndex, 1);
5673         this.config.splice(newIndex, 0, c);
5674         this.dataMap = null;
5675         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5676     },
5677
5678     isLocked : function(colIndex){
5679         return this.config[colIndex].locked === true;
5680     },
5681
5682     setLocked : function(colIndex, value, suppressEvent){
5683         if(this.isLocked(colIndex) == value){
5684             return;
5685         }
5686         this.config[colIndex].locked = value;
5687         if(!suppressEvent){
5688             this.fireEvent("columnlockchange", this, colIndex, value);
5689         }
5690     },
5691
5692     getTotalLockedWidth : function(){
5693         var totalWidth = 0;
5694         for(var i = 0; i < this.config.length; i++){
5695             if(this.isLocked(i) && !this.isHidden(i)){
5696                 this.totalWidth += this.getColumnWidth(i);
5697             }
5698         }
5699         return totalWidth;
5700     },
5701
5702     getLockedCount : function(){
5703         for(var i = 0, len = this.config.length; i < len; i++){
5704             if(!this.isLocked(i)){
5705                 return i;
5706             }
5707         }
5708         
5709         return this.config.length;
5710     },
5711
5712     /**
5713      * Returns the number of columns.
5714      * @return {Number}
5715      */
5716     getColumnCount : function(visibleOnly){
5717         if(visibleOnly === true){
5718             var c = 0;
5719             for(var i = 0, len = this.config.length; i < len; i++){
5720                 if(!this.isHidden(i)){
5721                     c++;
5722                 }
5723             }
5724             return c;
5725         }
5726         return this.config.length;
5727     },
5728
5729     /**
5730      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5731      * @param {Function} fn
5732      * @param {Object} scope (optional)
5733      * @return {Array} result
5734      */
5735     getColumnsBy : function(fn, scope){
5736         var r = [];
5737         for(var i = 0, len = this.config.length; i < len; i++){
5738             var c = this.config[i];
5739             if(fn.call(scope||this, c, i) === true){
5740                 r[r.length] = c;
5741             }
5742         }
5743         return r;
5744     },
5745
5746     /**
5747      * Returns true if the specified column is sortable.
5748      * @param {Number} col The column index
5749      * @return {Boolean}
5750      */
5751     isSortable : function(col){
5752         if(typeof this.config[col].sortable == "undefined"){
5753             return this.defaultSortable;
5754         }
5755         return this.config[col].sortable;
5756     },
5757
5758     /**
5759      * Returns the rendering (formatting) function defined for the column.
5760      * @param {Number} col The column index.
5761      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5762      */
5763     getRenderer : function(col){
5764         if(!this.config[col].renderer){
5765             return Roo.grid.ColumnModel.defaultRenderer;
5766         }
5767         return this.config[col].renderer;
5768     },
5769
5770     /**
5771      * Sets the rendering (formatting) function for a column.
5772      * @param {Number} col The column index
5773      * @param {Function} fn The function to use to process the cell's raw data
5774      * to return HTML markup for the grid view. The render function is called with
5775      * the following parameters:<ul>
5776      * <li>Data value.</li>
5777      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5778      * <li>css A CSS style string to apply to the table cell.</li>
5779      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5780      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5781      * <li>Row index</li>
5782      * <li>Column index</li>
5783      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5784      */
5785     setRenderer : function(col, fn){
5786         this.config[col].renderer = fn;
5787     },
5788
5789     /**
5790      * Returns the width for the specified column.
5791      * @param {Number} col The column index
5792      * @return {Number}
5793      */
5794     getColumnWidth : function(col){
5795         return this.config[col].width * 1 || this.defaultWidth;
5796     },
5797
5798     /**
5799      * Sets the width for a column.
5800      * @param {Number} col The column index
5801      * @param {Number} width The new width
5802      */
5803     setColumnWidth : function(col, width, suppressEvent){
5804         this.config[col].width = width;
5805         this.totalWidth = null;
5806         if(!suppressEvent){
5807              this.fireEvent("widthchange", this, col, width);
5808         }
5809     },
5810
5811     /**
5812      * Returns the total width of all columns.
5813      * @param {Boolean} includeHidden True to include hidden column widths
5814      * @return {Number}
5815      */
5816     getTotalWidth : function(includeHidden){
5817         if(!this.totalWidth){
5818             this.totalWidth = 0;
5819             for(var i = 0, len = this.config.length; i < len; i++){
5820                 if(includeHidden || !this.isHidden(i)){
5821                     this.totalWidth += this.getColumnWidth(i);
5822                 }
5823             }
5824         }
5825         return this.totalWidth;
5826     },
5827
5828     /**
5829      * Returns the header for the specified column.
5830      * @param {Number} col The column index
5831      * @return {String}
5832      */
5833     getColumnHeader : function(col){
5834         return this.config[col].header;
5835     },
5836
5837     /**
5838      * Sets the header for a column.
5839      * @param {Number} col The column index
5840      * @param {String} header The new header
5841      */
5842     setColumnHeader : function(col, header){
5843         this.config[col].header = header;
5844         this.fireEvent("headerchange", this, col, header);
5845     },
5846
5847     /**
5848      * Returns the tooltip for the specified column.
5849      * @param {Number} col The column index
5850      * @return {String}
5851      */
5852     getColumnTooltip : function(col){
5853             return this.config[col].tooltip;
5854     },
5855     /**
5856      * Sets the tooltip for a column.
5857      * @param {Number} col The column index
5858      * @param {String} tooltip The new tooltip
5859      */
5860     setColumnTooltip : function(col, tooltip){
5861             this.config[col].tooltip = tooltip;
5862     },
5863
5864     /**
5865      * Returns the dataIndex for the specified column.
5866      * @param {Number} col The column index
5867      * @return {Number}
5868      */
5869     getDataIndex : function(col){
5870         return this.config[col].dataIndex;
5871     },
5872
5873     /**
5874      * Sets the dataIndex for a column.
5875      * @param {Number} col The column index
5876      * @param {Number} dataIndex The new dataIndex
5877      */
5878     setDataIndex : function(col, dataIndex){
5879         this.config[col].dataIndex = dataIndex;
5880     },
5881
5882     
5883     
5884     /**
5885      * Returns true if the cell is editable.
5886      * @param {Number} colIndex The column index
5887      * @param {Number} rowIndex The row index - this is nto actually used..?
5888      * @return {Boolean}
5889      */
5890     isCellEditable : function(colIndex, rowIndex){
5891         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5892     },
5893
5894     /**
5895      * Returns the editor defined for the cell/column.
5896      * return false or null to disable editing.
5897      * @param {Number} colIndex The column index
5898      * @param {Number} rowIndex The row index
5899      * @return {Object}
5900      */
5901     getCellEditor : function(colIndex, rowIndex){
5902         return this.config[colIndex].editor;
5903     },
5904
5905     /**
5906      * Sets if a column is editable.
5907      * @param {Number} col The column index
5908      * @param {Boolean} editable True if the column is editable
5909      */
5910     setEditable : function(col, editable){
5911         this.config[col].editable = editable;
5912     },
5913
5914
5915     /**
5916      * Returns true if the column is hidden.
5917      * @param {Number} colIndex The column index
5918      * @return {Boolean}
5919      */
5920     isHidden : function(colIndex){
5921         return this.config[colIndex].hidden;
5922     },
5923
5924
5925     /**
5926      * Returns true if the column width cannot be changed
5927      */
5928     isFixed : function(colIndex){
5929         return this.config[colIndex].fixed;
5930     },
5931
5932     /**
5933      * Returns true if the column can be resized
5934      * @return {Boolean}
5935      */
5936     isResizable : function(colIndex){
5937         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5938     },
5939     /**
5940      * Sets if a column is hidden.
5941      * @param {Number} colIndex The column index
5942      * @param {Boolean} hidden True if the column is hidden
5943      */
5944     setHidden : function(colIndex, hidden){
5945         this.config[colIndex].hidden = hidden;
5946         this.totalWidth = null;
5947         this.fireEvent("hiddenchange", this, colIndex, hidden);
5948     },
5949
5950     /**
5951      * Sets the editor for a column.
5952      * @param {Number} col The column index
5953      * @param {Object} editor The editor object
5954      */
5955     setEditor : function(col, editor){
5956         this.config[col].editor = editor;
5957     }
5958 });
5959
5960 Roo.grid.ColumnModel.defaultRenderer = function(value)
5961 {
5962     if(typeof value == "object") {
5963         return value;
5964     }
5965         if(typeof value == "string" && value.length < 1){
5966             return "&#160;";
5967         }
5968     
5969         return String.format("{0}", value);
5970 };
5971
5972 // Alias for backwards compatibility
5973 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5974 /*
5975  * Based on:
5976  * Ext JS Library 1.1.1
5977  * Copyright(c) 2006-2007, Ext JS, LLC.
5978  *
5979  * Originally Released Under LGPL - original licence link has changed is not relivant.
5980  *
5981  * Fork - LGPL
5982  * <script type="text/javascript">
5983  */
5984  
5985 /**
5986  * @class Roo.LoadMask
5987  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5988  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5989  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5990  * element's UpdateManager load indicator and will be destroyed after the initial load.
5991  * @constructor
5992  * Create a new LoadMask
5993  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5994  * @param {Object} config The config object
5995  */
5996 Roo.LoadMask = function(el, config){
5997     this.el = Roo.get(el);
5998     Roo.apply(this, config);
5999     if(this.store){
6000         this.store.on('beforeload', this.onBeforeLoad, this);
6001         this.store.on('load', this.onLoad, this);
6002         this.store.on('loadexception', this.onLoadException, this);
6003         this.removeMask = false;
6004     }else{
6005         var um = this.el.getUpdateManager();
6006         um.showLoadIndicator = false; // disable the default indicator
6007         um.on('beforeupdate', this.onBeforeLoad, this);
6008         um.on('update', this.onLoad, this);
6009         um.on('failure', this.onLoad, this);
6010         this.removeMask = true;
6011     }
6012 };
6013
6014 Roo.LoadMask.prototype = {
6015     /**
6016      * @cfg {Boolean} removeMask
6017      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6018      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6019      */
6020     /**
6021      * @cfg {String} msg
6022      * The text to display in a centered loading message box (defaults to 'Loading...')
6023      */
6024     msg : 'Loading...',
6025     /**
6026      * @cfg {String} msgCls
6027      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6028      */
6029     msgCls : 'x-mask-loading',
6030
6031     /**
6032      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6033      * @type Boolean
6034      */
6035     disabled: false,
6036
6037     /**
6038      * Disables the mask to prevent it from being displayed
6039      */
6040     disable : function(){
6041        this.disabled = true;
6042     },
6043
6044     /**
6045      * Enables the mask so that it can be displayed
6046      */
6047     enable : function(){
6048         this.disabled = false;
6049     },
6050     
6051     onLoadException : function()
6052     {
6053         Roo.log(arguments);
6054         
6055         if (typeof(arguments[3]) != 'undefined') {
6056             Roo.MessageBox.alert("Error loading",arguments[3]);
6057         } 
6058         /*
6059         try {
6060             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6061                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6062             }   
6063         } catch(e) {
6064             
6065         }
6066         */
6067     
6068         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6069     },
6070     // private
6071     onLoad : function()
6072     {
6073         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6074     },
6075
6076     // private
6077     onBeforeLoad : function(){
6078         if(!this.disabled){
6079             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6080         }
6081     },
6082
6083     // private
6084     destroy : function(){
6085         if(this.store){
6086             this.store.un('beforeload', this.onBeforeLoad, this);
6087             this.store.un('load', this.onLoad, this);
6088             this.store.un('loadexception', this.onLoadException, this);
6089         }else{
6090             var um = this.el.getUpdateManager();
6091             um.un('beforeupdate', this.onBeforeLoad, this);
6092             um.un('update', this.onLoad, this);
6093             um.un('failure', this.onLoad, this);
6094         }
6095     }
6096 };/*
6097  * - LGPL
6098  *
6099  * table
6100  * 
6101  */
6102
6103 /**
6104  * @class Roo.bootstrap.Table
6105  * @extends Roo.bootstrap.Component
6106  * Bootstrap Table class
6107  * @cfg {String} cls table class
6108  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6109  * @cfg {String} bgcolor Specifies the background color for a table
6110  * @cfg {Number} border Specifies whether the table cells should have borders or not
6111  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6112  * @cfg {Number} cellspacing Specifies the space between cells
6113  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6114  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6115  * @cfg {String} sortable Specifies that the table should be sortable
6116  * @cfg {String} summary Specifies a summary of the content of a table
6117  * @cfg {Number} width Specifies the width of a table
6118  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6119  * 
6120  * @cfg {boolean} striped Should the rows be alternative striped
6121  * @cfg {boolean} bordered Add borders to the table
6122  * @cfg {boolean} hover Add hover highlighting
6123  * @cfg {boolean} condensed Format condensed
6124  * @cfg {boolean} responsive Format condensed
6125  * @cfg {Boolean} loadMask (true|false) default false
6126  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6127  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6128  * @cfg {Boolean} rowSelection (true|false) default false
6129  * @cfg {Boolean} cellSelection (true|false) default false
6130  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6131  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6132  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6133  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6134  
6135  * 
6136  * @constructor
6137  * Create a new Table
6138  * @param {Object} config The config object
6139  */
6140
6141 Roo.bootstrap.Table = function(config){
6142     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6143     
6144   
6145     
6146     // BC...
6147     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6148     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6149     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6150     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6151     
6152     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6153     if (this.sm) {
6154         this.sm.grid = this;
6155         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6156         this.sm = this.selModel;
6157         this.sm.xmodule = this.xmodule || false;
6158     }
6159     
6160     if (this.cm && typeof(this.cm.config) == 'undefined') {
6161         this.colModel = new Roo.grid.ColumnModel(this.cm);
6162         this.cm = this.colModel;
6163         this.cm.xmodule = this.xmodule || false;
6164     }
6165     if (this.store) {
6166         this.store= Roo.factory(this.store, Roo.data);
6167         this.ds = this.store;
6168         this.ds.xmodule = this.xmodule || false;
6169          
6170     }
6171     if (this.footer && this.store) {
6172         this.footer.dataSource = this.ds;
6173         this.footer = Roo.factory(this.footer);
6174     }
6175     
6176     /** @private */
6177     this.addEvents({
6178         /**
6179          * @event cellclick
6180          * Fires when a cell is clicked
6181          * @param {Roo.bootstrap.Table} this
6182          * @param {Roo.Element} el
6183          * @param {Number} rowIndex
6184          * @param {Number} columnIndex
6185          * @param {Roo.EventObject} e
6186          */
6187         "cellclick" : true,
6188         /**
6189          * @event celldblclick
6190          * Fires when a cell is double clicked
6191          * @param {Roo.bootstrap.Table} this
6192          * @param {Roo.Element} el
6193          * @param {Number} rowIndex
6194          * @param {Number} columnIndex
6195          * @param {Roo.EventObject} e
6196          */
6197         "celldblclick" : true,
6198         /**
6199          * @event rowclick
6200          * Fires when a row is clicked
6201          * @param {Roo.bootstrap.Table} this
6202          * @param {Roo.Element} el
6203          * @param {Number} rowIndex
6204          * @param {Roo.EventObject} e
6205          */
6206         "rowclick" : true,
6207         /**
6208          * @event rowdblclick
6209          * Fires when a row is double clicked
6210          * @param {Roo.bootstrap.Table} this
6211          * @param {Roo.Element} el
6212          * @param {Number} rowIndex
6213          * @param {Roo.EventObject} e
6214          */
6215         "rowdblclick" : true,
6216         /**
6217          * @event mouseover
6218          * Fires when a mouseover occur
6219          * @param {Roo.bootstrap.Table} this
6220          * @param {Roo.Element} el
6221          * @param {Number} rowIndex
6222          * @param {Number} columnIndex
6223          * @param {Roo.EventObject} e
6224          */
6225         "mouseover" : true,
6226         /**
6227          * @event mouseout
6228          * Fires when a mouseout occur
6229          * @param {Roo.bootstrap.Table} this
6230          * @param {Roo.Element} el
6231          * @param {Number} rowIndex
6232          * @param {Number} columnIndex
6233          * @param {Roo.EventObject} e
6234          */
6235         "mouseout" : true,
6236         /**
6237          * @event rowclass
6238          * Fires when a row is rendered, so you can change add a style to it.
6239          * @param {Roo.bootstrap.Table} this
6240          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6241          */
6242         'rowclass' : true,
6243           /**
6244          * @event rowsrendered
6245          * Fires when all the  rows have been rendered
6246          * @param {Roo.bootstrap.Table} this
6247          */
6248         'rowsrendered' : true,
6249         /**
6250          * @event contextmenu
6251          * The raw contextmenu event for the entire grid.
6252          * @param {Roo.EventObject} e
6253          */
6254         "contextmenu" : true,
6255         /**
6256          * @event rowcontextmenu
6257          * Fires when a row is right clicked
6258          * @param {Roo.bootstrap.Table} this
6259          * @param {Number} rowIndex
6260          * @param {Roo.EventObject} e
6261          */
6262         "rowcontextmenu" : true,
6263         /**
6264          * @event cellcontextmenu
6265          * Fires when a cell is right clicked
6266          * @param {Roo.bootstrap.Table} this
6267          * @param {Number} rowIndex
6268          * @param {Number} cellIndex
6269          * @param {Roo.EventObject} e
6270          */
6271          "cellcontextmenu" : true,
6272          /**
6273          * @event headercontextmenu
6274          * Fires when a header is right clicked
6275          * @param {Roo.bootstrap.Table} this
6276          * @param {Number} columnIndex
6277          * @param {Roo.EventObject} e
6278          */
6279         "headercontextmenu" : true
6280     });
6281 };
6282
6283 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6284     
6285     cls: false,
6286     align: false,
6287     bgcolor: false,
6288     border: false,
6289     cellpadding: false,
6290     cellspacing: false,
6291     frame: false,
6292     rules: false,
6293     sortable: false,
6294     summary: false,
6295     width: false,
6296     striped : false,
6297     scrollBody : false,
6298     bordered: false,
6299     hover:  false,
6300     condensed : false,
6301     responsive : false,
6302     sm : false,
6303     cm : false,
6304     store : false,
6305     loadMask : false,
6306     footerShow : true,
6307     headerShow : true,
6308   
6309     rowSelection : false,
6310     cellSelection : false,
6311     layout : false,
6312     
6313     // Roo.Element - the tbody
6314     mainBody: false,
6315     // Roo.Element - thead element
6316     mainHead: false,
6317     
6318     container: false, // used by gridpanel...
6319     
6320     lazyLoad : false,
6321     
6322     CSS : Roo.util.CSS,
6323     
6324     auto_hide_footer : false,
6325     
6326     getAutoCreate : function()
6327     {
6328         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6329         
6330         cfg = {
6331             tag: 'table',
6332             cls : 'table',
6333             cn : []
6334         };
6335         if (this.scrollBody) {
6336             cfg.cls += ' table-body-fixed';
6337         }    
6338         if (this.striped) {
6339             cfg.cls += ' table-striped';
6340         }
6341         
6342         if (this.hover) {
6343             cfg.cls += ' table-hover';
6344         }
6345         if (this.bordered) {
6346             cfg.cls += ' table-bordered';
6347         }
6348         if (this.condensed) {
6349             cfg.cls += ' table-condensed';
6350         }
6351         if (this.responsive) {
6352             cfg.cls += ' table-responsive';
6353         }
6354         
6355         if (this.cls) {
6356             cfg.cls+=  ' ' +this.cls;
6357         }
6358         
6359         // this lot should be simplifed...
6360         var _t = this;
6361         var cp = [
6362             'align',
6363             'bgcolor',
6364             'border',
6365             'cellpadding',
6366             'cellspacing',
6367             'frame',
6368             'rules',
6369             'sortable',
6370             'summary',
6371             'width'
6372         ].forEach(function(k) {
6373             if (_t[k]) {
6374                 cfg[k] = _t[k];
6375             }
6376         });
6377         
6378         
6379         if (this.layout) {
6380             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6381         }
6382         
6383         if(this.store || this.cm){
6384             if(this.headerShow){
6385                 cfg.cn.push(this.renderHeader());
6386             }
6387             
6388             cfg.cn.push(this.renderBody());
6389             
6390             if(this.footerShow){
6391                 cfg.cn.push(this.renderFooter());
6392             }
6393             // where does this come from?
6394             //cfg.cls+=  ' TableGrid';
6395         }
6396         
6397         return { cn : [ cfg ] };
6398     },
6399     
6400     initEvents : function()
6401     {   
6402         if(!this.store || !this.cm){
6403             return;
6404         }
6405         if (this.selModel) {
6406             this.selModel.initEvents();
6407         }
6408         
6409         
6410         //Roo.log('initEvents with ds!!!!');
6411         
6412         this.mainBody = this.el.select('tbody', true).first();
6413         this.mainHead = this.el.select('thead', true).first();
6414         this.mainFoot = this.el.select('tfoot', true).first();
6415         
6416         
6417         
6418         var _this = this;
6419         
6420         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6421             e.on('click', _this.sort, _this);
6422         });
6423         
6424         this.mainBody.on("click", this.onClick, this);
6425         this.mainBody.on("dblclick", this.onDblClick, this);
6426         
6427         // why is this done????? = it breaks dialogs??
6428         //this.parent().el.setStyle('position', 'relative');
6429         
6430         
6431         if (this.footer) {
6432             this.footer.parentId = this.id;
6433             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6434             
6435             if(this.lazyLoad){
6436                 this.el.select('tfoot tr td').first().addClass('hide');
6437             }
6438         } 
6439         
6440         if(this.loadMask) {
6441             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6442         }
6443         
6444         this.store.on('load', this.onLoad, this);
6445         this.store.on('beforeload', this.onBeforeLoad, this);
6446         this.store.on('update', this.onUpdate, this);
6447         this.store.on('add', this.onAdd, this);
6448         this.store.on("clear", this.clear, this);
6449         
6450         this.el.on("contextmenu", this.onContextMenu, this);
6451         
6452         this.mainBody.on('scroll', this.onBodyScroll, this);
6453         
6454         this.cm.on("headerchange", this.onHeaderChange, this);
6455         
6456         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6457         
6458     },
6459     
6460     onContextMenu : function(e, t)
6461     {
6462         this.processEvent("contextmenu", e);
6463     },
6464     
6465     processEvent : function(name, e)
6466     {
6467         if (name != 'touchstart' ) {
6468             this.fireEvent(name, e);    
6469         }
6470         
6471         var t = e.getTarget();
6472         
6473         var cell = Roo.get(t);
6474         
6475         if(!cell){
6476             return;
6477         }
6478         
6479         if(cell.findParent('tfoot', false, true)){
6480             return;
6481         }
6482         
6483         if(cell.findParent('thead', false, true)){
6484             
6485             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6486                 cell = Roo.get(t).findParent('th', false, true);
6487                 if (!cell) {
6488                     Roo.log("failed to find th in thead?");
6489                     Roo.log(e.getTarget());
6490                     return;
6491                 }
6492             }
6493             
6494             var cellIndex = cell.dom.cellIndex;
6495             
6496             var ename = name == 'touchstart' ? 'click' : name;
6497             this.fireEvent("header" + ename, this, cellIndex, e);
6498             
6499             return;
6500         }
6501         
6502         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6503             cell = Roo.get(t).findParent('td', false, true);
6504             if (!cell) {
6505                 Roo.log("failed to find th in tbody?");
6506                 Roo.log(e.getTarget());
6507                 return;
6508             }
6509         }
6510         
6511         var row = cell.findParent('tr', false, true);
6512         var cellIndex = cell.dom.cellIndex;
6513         var rowIndex = row.dom.rowIndex - 1;
6514         
6515         if(row !== false){
6516             
6517             this.fireEvent("row" + name, this, rowIndex, e);
6518             
6519             if(cell !== false){
6520             
6521                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6522             }
6523         }
6524         
6525     },
6526     
6527     onMouseover : function(e, el)
6528     {
6529         var cell = Roo.get(el);
6530         
6531         if(!cell){
6532             return;
6533         }
6534         
6535         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6536             cell = cell.findParent('td', false, true);
6537         }
6538         
6539         var row = cell.findParent('tr', false, true);
6540         var cellIndex = cell.dom.cellIndex;
6541         var rowIndex = row.dom.rowIndex - 1; // start from 0
6542         
6543         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6544         
6545     },
6546     
6547     onMouseout : function(e, el)
6548     {
6549         var cell = Roo.get(el);
6550         
6551         if(!cell){
6552             return;
6553         }
6554         
6555         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6556             cell = cell.findParent('td', false, true);
6557         }
6558         
6559         var row = cell.findParent('tr', false, true);
6560         var cellIndex = cell.dom.cellIndex;
6561         var rowIndex = row.dom.rowIndex - 1; // start from 0
6562         
6563         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6564         
6565     },
6566     
6567     onClick : function(e, el)
6568     {
6569         var cell = Roo.get(el);
6570         
6571         if(!cell || (!this.cellSelection && !this.rowSelection)){
6572             return;
6573         }
6574         
6575         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6576             cell = cell.findParent('td', false, true);
6577         }
6578         
6579         if(!cell || typeof(cell) == 'undefined'){
6580             return;
6581         }
6582         
6583         var row = cell.findParent('tr', false, true);
6584         
6585         if(!row || typeof(row) == 'undefined'){
6586             return;
6587         }
6588         
6589         var cellIndex = cell.dom.cellIndex;
6590         var rowIndex = this.getRowIndex(row);
6591         
6592         // why??? - should these not be based on SelectionModel?
6593         if(this.cellSelection){
6594             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6595         }
6596         
6597         if(this.rowSelection){
6598             this.fireEvent('rowclick', this, row, rowIndex, e);
6599         }
6600         
6601         
6602     },
6603         
6604     onDblClick : function(e,el)
6605     {
6606         var cell = Roo.get(el);
6607         
6608         if(!cell || (!this.cellSelection && !this.rowSelection)){
6609             return;
6610         }
6611         
6612         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6613             cell = cell.findParent('td', false, true);
6614         }
6615         
6616         if(!cell || typeof(cell) == 'undefined'){
6617             return;
6618         }
6619         
6620         var row = cell.findParent('tr', false, true);
6621         
6622         if(!row || typeof(row) == 'undefined'){
6623             return;
6624         }
6625         
6626         var cellIndex = cell.dom.cellIndex;
6627         var rowIndex = this.getRowIndex(row);
6628         
6629         if(this.cellSelection){
6630             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6631         }
6632         
6633         if(this.rowSelection){
6634             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6635         }
6636     },
6637     
6638     sort : function(e,el)
6639     {
6640         var col = Roo.get(el);
6641         
6642         if(!col.hasClass('sortable')){
6643             return;
6644         }
6645         
6646         var sort = col.attr('sort');
6647         var dir = 'ASC';
6648         
6649         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6650             dir = 'DESC';
6651         }
6652         
6653         this.store.sortInfo = {field : sort, direction : dir};
6654         
6655         if (this.footer) {
6656             Roo.log("calling footer first");
6657             this.footer.onClick('first');
6658         } else {
6659         
6660             this.store.load({ params : { start : 0 } });
6661         }
6662     },
6663     
6664     renderHeader : function()
6665     {
6666         var header = {
6667             tag: 'thead',
6668             cn : []
6669         };
6670         
6671         var cm = this.cm;
6672         this.totalWidth = 0;
6673         
6674         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6675             
6676             var config = cm.config[i];
6677             
6678             var c = {
6679                 tag: 'th',
6680                 cls : 'x-hcol-' + i,
6681                 style : '',
6682                 html: cm.getColumnHeader(i)
6683             };
6684             
6685             var hh = '';
6686             
6687             if(typeof(config.sortable) != 'undefined' && config.sortable){
6688                 c.cls = 'sortable';
6689                 c.html = '<i class="glyphicon"></i>' + c.html;
6690             }
6691             
6692             if(typeof(config.lgHeader) != 'undefined'){
6693                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6694             }
6695             
6696             if(typeof(config.mdHeader) != 'undefined'){
6697                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6698             }
6699             
6700             if(typeof(config.smHeader) != 'undefined'){
6701                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6702             }
6703             
6704             if(typeof(config.xsHeader) != 'undefined'){
6705                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6706             }
6707             
6708             if(hh.length){
6709                 c.html = hh;
6710             }
6711             
6712             if(typeof(config.tooltip) != 'undefined'){
6713                 c.tooltip = config.tooltip;
6714             }
6715             
6716             if(typeof(config.colspan) != 'undefined'){
6717                 c.colspan = config.colspan;
6718             }
6719             
6720             if(typeof(config.hidden) != 'undefined' && config.hidden){
6721                 c.style += ' display:none;';
6722             }
6723             
6724             if(typeof(config.dataIndex) != 'undefined'){
6725                 c.sort = config.dataIndex;
6726             }
6727             
6728            
6729             
6730             if(typeof(config.align) != 'undefined' && config.align.length){
6731                 c.style += ' text-align:' + config.align + ';';
6732             }
6733             
6734             if(typeof(config.width) != 'undefined'){
6735                 c.style += ' width:' + config.width + 'px;';
6736                 this.totalWidth += config.width;
6737             } else {
6738                 this.totalWidth += 100; // assume minimum of 100 per column?
6739             }
6740             
6741             if(typeof(config.cls) != 'undefined'){
6742                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6743             }
6744             
6745             ['xs','sm','md','lg'].map(function(size){
6746                 
6747                 if(typeof(config[size]) == 'undefined'){
6748                     return;
6749                 }
6750                 
6751                 if (!config[size]) { // 0 = hidden
6752                     c.cls += ' hidden-' + size;
6753                     return;
6754                 }
6755                 
6756                 c.cls += ' col-' + size + '-' + config[size];
6757
6758             });
6759             
6760             header.cn.push(c)
6761         }
6762         
6763         return header;
6764     },
6765     
6766     renderBody : function()
6767     {
6768         var body = {
6769             tag: 'tbody',
6770             cn : [
6771                 {
6772                     tag: 'tr',
6773                     cn : [
6774                         {
6775                             tag : 'td',
6776                             colspan :  this.cm.getColumnCount()
6777                         }
6778                     ]
6779                 }
6780             ]
6781         };
6782         
6783         return body;
6784     },
6785     
6786     renderFooter : function()
6787     {
6788         var footer = {
6789             tag: 'tfoot',
6790             cn : [
6791                 {
6792                     tag: 'tr',
6793                     cn : [
6794                         {
6795                             tag : 'td',
6796                             colspan :  this.cm.getColumnCount()
6797                         }
6798                     ]
6799                 }
6800             ]
6801         };
6802         
6803         return footer;
6804     },
6805     
6806     
6807     
6808     onLoad : function()
6809     {
6810 //        Roo.log('ds onload');
6811         this.clear();
6812         
6813         var _this = this;
6814         var cm = this.cm;
6815         var ds = this.store;
6816         
6817         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6818             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6819             if (_this.store.sortInfo) {
6820                     
6821                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6822                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6823                 }
6824                 
6825                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6826                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6827                 }
6828             }
6829         });
6830         
6831         var tbody =  this.mainBody;
6832               
6833         if(ds.getCount() > 0){
6834             ds.data.each(function(d,rowIndex){
6835                 var row =  this.renderRow(cm, ds, rowIndex);
6836                 
6837                 tbody.createChild(row);
6838                 
6839                 var _this = this;
6840                 
6841                 if(row.cellObjects.length){
6842                     Roo.each(row.cellObjects, function(r){
6843                         _this.renderCellObject(r);
6844                     })
6845                 }
6846                 
6847             }, this);
6848         }
6849         
6850         var tfoot = this.el.select('tfoot', true).first();
6851         
6852         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6853             
6854             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6855             
6856             var total = this.ds.getTotalCount();
6857             
6858             if(this.footer.pageSize < total){
6859                 this.mainFoot.show();
6860             }
6861         }
6862         
6863         Roo.each(this.el.select('tbody td', true).elements, function(e){
6864             e.on('mouseover', _this.onMouseover, _this);
6865         });
6866         
6867         Roo.each(this.el.select('tbody td', true).elements, function(e){
6868             e.on('mouseout', _this.onMouseout, _this);
6869         });
6870         this.fireEvent('rowsrendered', this);
6871         
6872         this.autoSize();
6873     },
6874     
6875     
6876     onUpdate : function(ds,record)
6877     {
6878         this.refreshRow(record);
6879         this.autoSize();
6880     },
6881     
6882     onRemove : function(ds, record, index, isUpdate){
6883         if(isUpdate !== true){
6884             this.fireEvent("beforerowremoved", this, index, record);
6885         }
6886         var bt = this.mainBody.dom;
6887         
6888         var rows = this.el.select('tbody > tr', true).elements;
6889         
6890         if(typeof(rows[index]) != 'undefined'){
6891             bt.removeChild(rows[index].dom);
6892         }
6893         
6894 //        if(bt.rows[index]){
6895 //            bt.removeChild(bt.rows[index]);
6896 //        }
6897         
6898         if(isUpdate !== true){
6899             //this.stripeRows(index);
6900             //this.syncRowHeights(index, index);
6901             //this.layout();
6902             this.fireEvent("rowremoved", this, index, record);
6903         }
6904     },
6905     
6906     onAdd : function(ds, records, rowIndex)
6907     {
6908         //Roo.log('on Add called');
6909         // - note this does not handle multiple adding very well..
6910         var bt = this.mainBody.dom;
6911         for (var i =0 ; i < records.length;i++) {
6912             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6913             //Roo.log(records[i]);
6914             //Roo.log(this.store.getAt(rowIndex+i));
6915             this.insertRow(this.store, rowIndex + i, false);
6916             return;
6917         }
6918         
6919     },
6920     
6921     
6922     refreshRow : function(record){
6923         var ds = this.store, index;
6924         if(typeof record == 'number'){
6925             index = record;
6926             record = ds.getAt(index);
6927         }else{
6928             index = ds.indexOf(record);
6929         }
6930         this.insertRow(ds, index, true);
6931         this.autoSize();
6932         this.onRemove(ds, record, index+1, true);
6933         this.autoSize();
6934         //this.syncRowHeights(index, index);
6935         //this.layout();
6936         this.fireEvent("rowupdated", this, index, record);
6937     },
6938     
6939     insertRow : function(dm, rowIndex, isUpdate){
6940         
6941         if(!isUpdate){
6942             this.fireEvent("beforerowsinserted", this, rowIndex);
6943         }
6944             //var s = this.getScrollState();
6945         var row = this.renderRow(this.cm, this.store, rowIndex);
6946         // insert before rowIndex..
6947         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6948         
6949         var _this = this;
6950                 
6951         if(row.cellObjects.length){
6952             Roo.each(row.cellObjects, function(r){
6953                 _this.renderCellObject(r);
6954             })
6955         }
6956             
6957         if(!isUpdate){
6958             this.fireEvent("rowsinserted", this, rowIndex);
6959             //this.syncRowHeights(firstRow, lastRow);
6960             //this.stripeRows(firstRow);
6961             //this.layout();
6962         }
6963         
6964     },
6965     
6966     
6967     getRowDom : function(rowIndex)
6968     {
6969         var rows = this.el.select('tbody > tr', true).elements;
6970         
6971         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6972         
6973     },
6974     // returns the object tree for a tr..
6975   
6976     
6977     renderRow : function(cm, ds, rowIndex) 
6978     {
6979         var d = ds.getAt(rowIndex);
6980         
6981         var row = {
6982             tag : 'tr',
6983             cls : 'x-row-' + rowIndex,
6984             cn : []
6985         };
6986             
6987         var cellObjects = [];
6988         
6989         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6990             var config = cm.config[i];
6991             
6992             var renderer = cm.getRenderer(i);
6993             var value = '';
6994             var id = false;
6995             
6996             if(typeof(renderer) !== 'undefined'){
6997                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6998             }
6999             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7000             // and are rendered into the cells after the row is rendered - using the id for the element.
7001             
7002             if(typeof(value) === 'object'){
7003                 id = Roo.id();
7004                 cellObjects.push({
7005                     container : id,
7006                     cfg : value 
7007                 })
7008             }
7009             
7010             var rowcfg = {
7011                 record: d,
7012                 rowIndex : rowIndex,
7013                 colIndex : i,
7014                 rowClass : ''
7015             };
7016
7017             this.fireEvent('rowclass', this, rowcfg);
7018             
7019             var td = {
7020                 tag: 'td',
7021                 cls : rowcfg.rowClass + ' x-col-' + i,
7022                 style: '',
7023                 html: (typeof(value) === 'object') ? '' : value
7024             };
7025             
7026             if (id) {
7027                 td.id = id;
7028             }
7029             
7030             if(typeof(config.colspan) != 'undefined'){
7031                 td.colspan = config.colspan;
7032             }
7033             
7034             if(typeof(config.hidden) != 'undefined' && config.hidden){
7035                 td.style += ' display:none;';
7036             }
7037             
7038             if(typeof(config.align) != 'undefined' && config.align.length){
7039                 td.style += ' text-align:' + config.align + ';';
7040             }
7041             if(typeof(config.valign) != 'undefined' && config.valign.length){
7042                 td.style += ' vertical-align:' + config.valign + ';';
7043             }
7044             
7045             if(typeof(config.width) != 'undefined'){
7046                 td.style += ' width:' +  config.width + 'px;';
7047             }
7048             
7049             if(typeof(config.cursor) != 'undefined'){
7050                 td.style += ' cursor:' +  config.cursor + ';';
7051             }
7052             
7053             if(typeof(config.cls) != 'undefined'){
7054                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7055             }
7056             
7057             ['xs','sm','md','lg'].map(function(size){
7058                 
7059                 if(typeof(config[size]) == 'undefined'){
7060                     return;
7061                 }
7062                 
7063                 if (!config[size]) { // 0 = hidden
7064                     td.cls += ' hidden-' + size;
7065                     return;
7066                 }
7067                 
7068                 td.cls += ' col-' + size + '-' + config[size];
7069
7070             });
7071             
7072             row.cn.push(td);
7073            
7074         }
7075         
7076         row.cellObjects = cellObjects;
7077         
7078         return row;
7079           
7080     },
7081     
7082     
7083     
7084     onBeforeLoad : function()
7085     {
7086         
7087     },
7088      /**
7089      * Remove all rows
7090      */
7091     clear : function()
7092     {
7093         this.el.select('tbody', true).first().dom.innerHTML = '';
7094     },
7095     /**
7096      * Show or hide a row.
7097      * @param {Number} rowIndex to show or hide
7098      * @param {Boolean} state hide
7099      */
7100     setRowVisibility : function(rowIndex, state)
7101     {
7102         var bt = this.mainBody.dom;
7103         
7104         var rows = this.el.select('tbody > tr', true).elements;
7105         
7106         if(typeof(rows[rowIndex]) == 'undefined'){
7107             return;
7108         }
7109         rows[rowIndex].dom.style.display = state ? '' : 'none';
7110     },
7111     
7112     
7113     getSelectionModel : function(){
7114         if(!this.selModel){
7115             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7116         }
7117         return this.selModel;
7118     },
7119     /*
7120      * Render the Roo.bootstrap object from renderder
7121      */
7122     renderCellObject : function(r)
7123     {
7124         var _this = this;
7125         
7126         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7127         
7128         var t = r.cfg.render(r.container);
7129         
7130         if(r.cfg.cn){
7131             Roo.each(r.cfg.cn, function(c){
7132                 var child = {
7133                     container: t.getChildContainer(),
7134                     cfg: c
7135                 };
7136                 _this.renderCellObject(child);
7137             })
7138         }
7139     },
7140     
7141     getRowIndex : function(row)
7142     {
7143         var rowIndex = -1;
7144         
7145         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7146             if(el != row){
7147                 return;
7148             }
7149             
7150             rowIndex = index;
7151         });
7152         
7153         return rowIndex;
7154     },
7155      /**
7156      * Returns the grid's underlying element = used by panel.Grid
7157      * @return {Element} The element
7158      */
7159     getGridEl : function(){
7160         return this.el;
7161     },
7162      /**
7163      * Forces a resize - used by panel.Grid
7164      * @return {Element} The element
7165      */
7166     autoSize : function()
7167     {
7168         //var ctr = Roo.get(this.container.dom.parentElement);
7169         var ctr = Roo.get(this.el.dom);
7170         
7171         var thd = this.getGridEl().select('thead',true).first();
7172         var tbd = this.getGridEl().select('tbody', true).first();
7173         var tfd = this.getGridEl().select('tfoot', true).first();
7174         
7175         var cw = ctr.getWidth();
7176         
7177         if (tbd) {
7178             
7179             tbd.setSize(ctr.getWidth(),
7180                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7181             );
7182             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7183             cw -= barsize;
7184         }
7185         cw = Math.max(cw, this.totalWidth);
7186         this.getGridEl().select('tr',true).setWidth(cw);
7187         // resize 'expandable coloumn?
7188         
7189         return; // we doe not have a view in this design..
7190         
7191     },
7192     onBodyScroll: function()
7193     {
7194         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7195         if(this.mainHead){
7196             this.mainHead.setStyle({
7197                 'position' : 'relative',
7198                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7199             });
7200         }
7201         
7202         if(this.lazyLoad){
7203             
7204             var scrollHeight = this.mainBody.dom.scrollHeight;
7205             
7206             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7207             
7208             var height = this.mainBody.getHeight();
7209             
7210             if(scrollHeight - height == scrollTop) {
7211                 
7212                 var total = this.ds.getTotalCount();
7213                 
7214                 if(this.footer.cursor + this.footer.pageSize < total){
7215                     
7216                     this.footer.ds.load({
7217                         params : {
7218                             start : this.footer.cursor + this.footer.pageSize,
7219                             limit : this.footer.pageSize
7220                         },
7221                         add : true
7222                     });
7223                 }
7224             }
7225             
7226         }
7227     },
7228     
7229     onHeaderChange : function()
7230     {
7231         var header = this.renderHeader();
7232         var table = this.el.select('table', true).first();
7233         
7234         this.mainHead.remove();
7235         this.mainHead = table.createChild(header, this.mainBody, false);
7236     },
7237     
7238     onHiddenChange : function(colModel, colIndex, hidden)
7239     {
7240         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7241         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7242         
7243         this.CSS.updateRule(thSelector, "display", "");
7244         this.CSS.updateRule(tdSelector, "display", "");
7245         
7246         if(hidden){
7247             this.CSS.updateRule(thSelector, "display", "none");
7248             this.CSS.updateRule(tdSelector, "display", "none");
7249         }
7250         
7251         this.onHeaderChange();
7252         this.onLoad();
7253     },
7254     
7255     setColumnWidth: function(col_index, width)
7256     {
7257         // width = "md-2 xs-2..."
7258         if(!this.colModel.config[col_index]) {
7259             return;
7260         }
7261         
7262         var w = width.split(" ");
7263         
7264         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7265         
7266         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7267         
7268         
7269         for(var j = 0; j < w.length; j++) {
7270             
7271             if(!w[j]) {
7272                 continue;
7273             }
7274             
7275             var size_cls = w[j].split("-");
7276             
7277             if(!Number.isInteger(size_cls[1] * 1)) {
7278                 continue;
7279             }
7280             
7281             if(!this.colModel.config[col_index][size_cls[0]]) {
7282                 continue;
7283             }
7284             
7285             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7286                 continue;
7287             }
7288             
7289             h_row[0].classList.replace(
7290                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7291                 "col-"+size_cls[0]+"-"+size_cls[1]
7292             );
7293             
7294             for(var i = 0; i < rows.length; i++) {
7295                 
7296                 var size_cls = w[j].split("-");
7297                 
7298                 if(!Number.isInteger(size_cls[1] * 1)) {
7299                     continue;
7300                 }
7301                 
7302                 if(!this.colModel.config[col_index][size_cls[0]]) {
7303                     continue;
7304                 }
7305                 
7306                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7307                     continue;
7308                 }
7309                 
7310                 rows[i].classList.replace(
7311                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7312                     "col-"+size_cls[0]+"-"+size_cls[1]
7313                 );
7314             }
7315             
7316             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7317         }
7318     }
7319 });
7320
7321  
7322
7323  /*
7324  * - LGPL
7325  *
7326  * table cell
7327  * 
7328  */
7329
7330 /**
7331  * @class Roo.bootstrap.TableCell
7332  * @extends Roo.bootstrap.Component
7333  * Bootstrap TableCell class
7334  * @cfg {String} html cell contain text
7335  * @cfg {String} cls cell class
7336  * @cfg {String} tag cell tag (td|th) default td
7337  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7338  * @cfg {String} align Aligns the content in a cell
7339  * @cfg {String} axis Categorizes cells
7340  * @cfg {String} bgcolor Specifies the background color of a cell
7341  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7342  * @cfg {Number} colspan Specifies the number of columns a cell should span
7343  * @cfg {String} headers Specifies one or more header cells a cell is related to
7344  * @cfg {Number} height Sets the height of a cell
7345  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7346  * @cfg {Number} rowspan Sets the number of rows a cell should span
7347  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7348  * @cfg {String} valign Vertical aligns the content in a cell
7349  * @cfg {Number} width Specifies the width of a cell
7350  * 
7351  * @constructor
7352  * Create a new TableCell
7353  * @param {Object} config The config object
7354  */
7355
7356 Roo.bootstrap.TableCell = function(config){
7357     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7358 };
7359
7360 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7361     
7362     html: false,
7363     cls: false,
7364     tag: false,
7365     abbr: false,
7366     align: false,
7367     axis: false,
7368     bgcolor: false,
7369     charoff: false,
7370     colspan: false,
7371     headers: false,
7372     height: false,
7373     nowrap: false,
7374     rowspan: false,
7375     scope: false,
7376     valign: false,
7377     width: false,
7378     
7379     
7380     getAutoCreate : function(){
7381         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7382         
7383         cfg = {
7384             tag: 'td'
7385         };
7386         
7387         if(this.tag){
7388             cfg.tag = this.tag;
7389         }
7390         
7391         if (this.html) {
7392             cfg.html=this.html
7393         }
7394         if (this.cls) {
7395             cfg.cls=this.cls
7396         }
7397         if (this.abbr) {
7398             cfg.abbr=this.abbr
7399         }
7400         if (this.align) {
7401             cfg.align=this.align
7402         }
7403         if (this.axis) {
7404             cfg.axis=this.axis
7405         }
7406         if (this.bgcolor) {
7407             cfg.bgcolor=this.bgcolor
7408         }
7409         if (this.charoff) {
7410             cfg.charoff=this.charoff
7411         }
7412         if (this.colspan) {
7413             cfg.colspan=this.colspan
7414         }
7415         if (this.headers) {
7416             cfg.headers=this.headers
7417         }
7418         if (this.height) {
7419             cfg.height=this.height
7420         }
7421         if (this.nowrap) {
7422             cfg.nowrap=this.nowrap
7423         }
7424         if (this.rowspan) {
7425             cfg.rowspan=this.rowspan
7426         }
7427         if (this.scope) {
7428             cfg.scope=this.scope
7429         }
7430         if (this.valign) {
7431             cfg.valign=this.valign
7432         }
7433         if (this.width) {
7434             cfg.width=this.width
7435         }
7436         
7437         
7438         return cfg;
7439     }
7440    
7441 });
7442
7443  
7444
7445  /*
7446  * - LGPL
7447  *
7448  * table row
7449  * 
7450  */
7451
7452 /**
7453  * @class Roo.bootstrap.TableRow
7454  * @extends Roo.bootstrap.Component
7455  * Bootstrap TableRow class
7456  * @cfg {String} cls row class
7457  * @cfg {String} align Aligns the content in a table row
7458  * @cfg {String} bgcolor Specifies a background color for a table row
7459  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7460  * @cfg {String} valign Vertical aligns the content in a table row
7461  * 
7462  * @constructor
7463  * Create a new TableRow
7464  * @param {Object} config The config object
7465  */
7466
7467 Roo.bootstrap.TableRow = function(config){
7468     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7469 };
7470
7471 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7472     
7473     cls: false,
7474     align: false,
7475     bgcolor: false,
7476     charoff: false,
7477     valign: false,
7478     
7479     getAutoCreate : function(){
7480         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7481         
7482         cfg = {
7483             tag: 'tr'
7484         };
7485             
7486         if(this.cls){
7487             cfg.cls = this.cls;
7488         }
7489         if(this.align){
7490             cfg.align = this.align;
7491         }
7492         if(this.bgcolor){
7493             cfg.bgcolor = this.bgcolor;
7494         }
7495         if(this.charoff){
7496             cfg.charoff = this.charoff;
7497         }
7498         if(this.valign){
7499             cfg.valign = this.valign;
7500         }
7501         
7502         return cfg;
7503     }
7504    
7505 });
7506
7507  
7508
7509  /*
7510  * - LGPL
7511  *
7512  * table body
7513  * 
7514  */
7515
7516 /**
7517  * @class Roo.bootstrap.TableBody
7518  * @extends Roo.bootstrap.Component
7519  * Bootstrap TableBody class
7520  * @cfg {String} cls element class
7521  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7522  * @cfg {String} align Aligns the content inside the element
7523  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7524  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7525  * 
7526  * @constructor
7527  * Create a new TableBody
7528  * @param {Object} config The config object
7529  */
7530
7531 Roo.bootstrap.TableBody = function(config){
7532     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7533 };
7534
7535 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7536     
7537     cls: false,
7538     tag: false,
7539     align: false,
7540     charoff: false,
7541     valign: false,
7542     
7543     getAutoCreate : function(){
7544         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7545         
7546         cfg = {
7547             tag: 'tbody'
7548         };
7549             
7550         if (this.cls) {
7551             cfg.cls=this.cls
7552         }
7553         if(this.tag){
7554             cfg.tag = this.tag;
7555         }
7556         
7557         if(this.align){
7558             cfg.align = this.align;
7559         }
7560         if(this.charoff){
7561             cfg.charoff = this.charoff;
7562         }
7563         if(this.valign){
7564             cfg.valign = this.valign;
7565         }
7566         
7567         return cfg;
7568     }
7569     
7570     
7571 //    initEvents : function()
7572 //    {
7573 //        
7574 //        if(!this.store){
7575 //            return;
7576 //        }
7577 //        
7578 //        this.store = Roo.factory(this.store, Roo.data);
7579 //        this.store.on('load', this.onLoad, this);
7580 //        
7581 //        this.store.load();
7582 //        
7583 //    },
7584 //    
7585 //    onLoad: function () 
7586 //    {   
7587 //        this.fireEvent('load', this);
7588 //    }
7589 //    
7590 //   
7591 });
7592
7593  
7594
7595  /*
7596  * Based on:
7597  * Ext JS Library 1.1.1
7598  * Copyright(c) 2006-2007, Ext JS, LLC.
7599  *
7600  * Originally Released Under LGPL - original licence link has changed is not relivant.
7601  *
7602  * Fork - LGPL
7603  * <script type="text/javascript">
7604  */
7605
7606 // as we use this in bootstrap.
7607 Roo.namespace('Roo.form');
7608  /**
7609  * @class Roo.form.Action
7610  * Internal Class used to handle form actions
7611  * @constructor
7612  * @param {Roo.form.BasicForm} el The form element or its id
7613  * @param {Object} config Configuration options
7614  */
7615
7616  
7617  
7618 // define the action interface
7619 Roo.form.Action = function(form, options){
7620     this.form = form;
7621     this.options = options || {};
7622 };
7623 /**
7624  * Client Validation Failed
7625  * @const 
7626  */
7627 Roo.form.Action.CLIENT_INVALID = 'client';
7628 /**
7629  * Server Validation Failed
7630  * @const 
7631  */
7632 Roo.form.Action.SERVER_INVALID = 'server';
7633  /**
7634  * Connect to Server Failed
7635  * @const 
7636  */
7637 Roo.form.Action.CONNECT_FAILURE = 'connect';
7638 /**
7639  * Reading Data from Server Failed
7640  * @const 
7641  */
7642 Roo.form.Action.LOAD_FAILURE = 'load';
7643
7644 Roo.form.Action.prototype = {
7645     type : 'default',
7646     failureType : undefined,
7647     response : undefined,
7648     result : undefined,
7649
7650     // interface method
7651     run : function(options){
7652
7653     },
7654
7655     // interface method
7656     success : function(response){
7657
7658     },
7659
7660     // interface method
7661     handleResponse : function(response){
7662
7663     },
7664
7665     // default connection failure
7666     failure : function(response){
7667         
7668         this.response = response;
7669         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7670         this.form.afterAction(this, false);
7671     },
7672
7673     processResponse : function(response){
7674         this.response = response;
7675         if(!response.responseText){
7676             return true;
7677         }
7678         this.result = this.handleResponse(response);
7679         return this.result;
7680     },
7681
7682     // utility functions used internally
7683     getUrl : function(appendParams){
7684         var url = this.options.url || this.form.url || this.form.el.dom.action;
7685         if(appendParams){
7686             var p = this.getParams();
7687             if(p){
7688                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7689             }
7690         }
7691         return url;
7692     },
7693
7694     getMethod : function(){
7695         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7696     },
7697
7698     getParams : function(){
7699         var bp = this.form.baseParams;
7700         var p = this.options.params;
7701         if(p){
7702             if(typeof p == "object"){
7703                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7704             }else if(typeof p == 'string' && bp){
7705                 p += '&' + Roo.urlEncode(bp);
7706             }
7707         }else if(bp){
7708             p = Roo.urlEncode(bp);
7709         }
7710         return p;
7711     },
7712
7713     createCallback : function(){
7714         return {
7715             success: this.success,
7716             failure: this.failure,
7717             scope: this,
7718             timeout: (this.form.timeout*1000),
7719             upload: this.form.fileUpload ? this.success : undefined
7720         };
7721     }
7722 };
7723
7724 Roo.form.Action.Submit = function(form, options){
7725     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7726 };
7727
7728 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7729     type : 'submit',
7730
7731     haveProgress : false,
7732     uploadComplete : false,
7733     
7734     // uploadProgress indicator.
7735     uploadProgress : function()
7736     {
7737         if (!this.form.progressUrl) {
7738             return;
7739         }
7740         
7741         if (!this.haveProgress) {
7742             Roo.MessageBox.progress("Uploading", "Uploading");
7743         }
7744         if (this.uploadComplete) {
7745            Roo.MessageBox.hide();
7746            return;
7747         }
7748         
7749         this.haveProgress = true;
7750    
7751         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7752         
7753         var c = new Roo.data.Connection();
7754         c.request({
7755             url : this.form.progressUrl,
7756             params: {
7757                 id : uid
7758             },
7759             method: 'GET',
7760             success : function(req){
7761                //console.log(data);
7762                 var rdata = false;
7763                 var edata;
7764                 try  {
7765                    rdata = Roo.decode(req.responseText)
7766                 } catch (e) {
7767                     Roo.log("Invalid data from server..");
7768                     Roo.log(edata);
7769                     return;
7770                 }
7771                 if (!rdata || !rdata.success) {
7772                     Roo.log(rdata);
7773                     Roo.MessageBox.alert(Roo.encode(rdata));
7774                     return;
7775                 }
7776                 var data = rdata.data;
7777                 
7778                 if (this.uploadComplete) {
7779                    Roo.MessageBox.hide();
7780                    return;
7781                 }
7782                    
7783                 if (data){
7784                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7785                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7786                     );
7787                 }
7788                 this.uploadProgress.defer(2000,this);
7789             },
7790        
7791             failure: function(data) {
7792                 Roo.log('progress url failed ');
7793                 Roo.log(data);
7794             },
7795             scope : this
7796         });
7797            
7798     },
7799     
7800     
7801     run : function()
7802     {
7803         // run get Values on the form, so it syncs any secondary forms.
7804         this.form.getValues();
7805         
7806         var o = this.options;
7807         var method = this.getMethod();
7808         var isPost = method == 'POST';
7809         if(o.clientValidation === false || this.form.isValid()){
7810             
7811             if (this.form.progressUrl) {
7812                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7813                     (new Date() * 1) + '' + Math.random());
7814                     
7815             } 
7816             
7817             
7818             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7819                 form:this.form.el.dom,
7820                 url:this.getUrl(!isPost),
7821                 method: method,
7822                 params:isPost ? this.getParams() : null,
7823                 isUpload: this.form.fileUpload
7824             }));
7825             
7826             this.uploadProgress();
7827
7828         }else if (o.clientValidation !== false){ // client validation failed
7829             this.failureType = Roo.form.Action.CLIENT_INVALID;
7830             this.form.afterAction(this, false);
7831         }
7832     },
7833
7834     success : function(response)
7835     {
7836         this.uploadComplete= true;
7837         if (this.haveProgress) {
7838             Roo.MessageBox.hide();
7839         }
7840         
7841         
7842         var result = this.processResponse(response);
7843         if(result === true || result.success){
7844             this.form.afterAction(this, true);
7845             return;
7846         }
7847         if(result.errors){
7848             this.form.markInvalid(result.errors);
7849             this.failureType = Roo.form.Action.SERVER_INVALID;
7850         }
7851         this.form.afterAction(this, false);
7852     },
7853     failure : function(response)
7854     {
7855         this.uploadComplete= true;
7856         if (this.haveProgress) {
7857             Roo.MessageBox.hide();
7858         }
7859         
7860         this.response = response;
7861         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7862         this.form.afterAction(this, false);
7863     },
7864     
7865     handleResponse : function(response){
7866         if(this.form.errorReader){
7867             var rs = this.form.errorReader.read(response);
7868             var errors = [];
7869             if(rs.records){
7870                 for(var i = 0, len = rs.records.length; i < len; i++) {
7871                     var r = rs.records[i];
7872                     errors[i] = r.data;
7873                 }
7874             }
7875             if(errors.length < 1){
7876                 errors = null;
7877             }
7878             return {
7879                 success : rs.success,
7880                 errors : errors
7881             };
7882         }
7883         var ret = false;
7884         try {
7885             ret = Roo.decode(response.responseText);
7886         } catch (e) {
7887             ret = {
7888                 success: false,
7889                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7890                 errors : []
7891             };
7892         }
7893         return ret;
7894         
7895     }
7896 });
7897
7898
7899 Roo.form.Action.Load = function(form, options){
7900     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7901     this.reader = this.form.reader;
7902 };
7903
7904 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7905     type : 'load',
7906
7907     run : function(){
7908         
7909         Roo.Ajax.request(Roo.apply(
7910                 this.createCallback(), {
7911                     method:this.getMethod(),
7912                     url:this.getUrl(false),
7913                     params:this.getParams()
7914         }));
7915     },
7916
7917     success : function(response){
7918         
7919         var result = this.processResponse(response);
7920         if(result === true || !result.success || !result.data){
7921             this.failureType = Roo.form.Action.LOAD_FAILURE;
7922             this.form.afterAction(this, false);
7923             return;
7924         }
7925         this.form.clearInvalid();
7926         this.form.setValues(result.data);
7927         this.form.afterAction(this, true);
7928     },
7929
7930     handleResponse : function(response){
7931         if(this.form.reader){
7932             var rs = this.form.reader.read(response);
7933             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7934             return {
7935                 success : rs.success,
7936                 data : data
7937             };
7938         }
7939         return Roo.decode(response.responseText);
7940     }
7941 });
7942
7943 Roo.form.Action.ACTION_TYPES = {
7944     'load' : Roo.form.Action.Load,
7945     'submit' : Roo.form.Action.Submit
7946 };/*
7947  * - LGPL
7948  *
7949  * form
7950  *
7951  */
7952
7953 /**
7954  * @class Roo.bootstrap.Form
7955  * @extends Roo.bootstrap.Component
7956  * Bootstrap Form class
7957  * @cfg {String} method  GET | POST (default POST)
7958  * @cfg {String} labelAlign top | left (default top)
7959  * @cfg {String} align left  | right - for navbars
7960  * @cfg {Boolean} loadMask load mask when submit (default true)
7961
7962  *
7963  * @constructor
7964  * Create a new Form
7965  * @param {Object} config The config object
7966  */
7967
7968
7969 Roo.bootstrap.Form = function(config){
7970     
7971     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7972     
7973     Roo.bootstrap.Form.popover.apply();
7974     
7975     this.addEvents({
7976         /**
7977          * @event clientvalidation
7978          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7979          * @param {Form} this
7980          * @param {Boolean} valid true if the form has passed client-side validation
7981          */
7982         clientvalidation: true,
7983         /**
7984          * @event beforeaction
7985          * Fires before any action is performed. Return false to cancel the action.
7986          * @param {Form} this
7987          * @param {Action} action The action to be performed
7988          */
7989         beforeaction: true,
7990         /**
7991          * @event actionfailed
7992          * Fires when an action fails.
7993          * @param {Form} this
7994          * @param {Action} action The action that failed
7995          */
7996         actionfailed : true,
7997         /**
7998          * @event actioncomplete
7999          * Fires when an action is completed.
8000          * @param {Form} this
8001          * @param {Action} action The action that completed
8002          */
8003         actioncomplete : true
8004     });
8005 };
8006
8007 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8008
8009      /**
8010      * @cfg {String} method
8011      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8012      */
8013     method : 'POST',
8014     /**
8015      * @cfg {String} url
8016      * The URL to use for form actions if one isn't supplied in the action options.
8017      */
8018     /**
8019      * @cfg {Boolean} fileUpload
8020      * Set to true if this form is a file upload.
8021      */
8022
8023     /**
8024      * @cfg {Object} baseParams
8025      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8026      */
8027
8028     /**
8029      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8030      */
8031     timeout: 30,
8032     /**
8033      * @cfg {Sting} align (left|right) for navbar forms
8034      */
8035     align : 'left',
8036
8037     // private
8038     activeAction : null,
8039
8040     /**
8041      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8042      * element by passing it or its id or mask the form itself by passing in true.
8043      * @type Mixed
8044      */
8045     waitMsgTarget : false,
8046
8047     loadMask : true,
8048     
8049     /**
8050      * @cfg {Boolean} errorMask (true|false) default false
8051      */
8052     errorMask : false,
8053     
8054     /**
8055      * @cfg {Number} maskOffset Default 100
8056      */
8057     maskOffset : 100,
8058     
8059     /**
8060      * @cfg {Boolean} maskBody
8061      */
8062     maskBody : false,
8063
8064     getAutoCreate : function(){
8065
8066         var cfg = {
8067             tag: 'form',
8068             method : this.method || 'POST',
8069             id : this.id || Roo.id(),
8070             cls : ''
8071         };
8072         if (this.parent().xtype.match(/^Nav/)) {
8073             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8074
8075         }
8076
8077         if (this.labelAlign == 'left' ) {
8078             cfg.cls += ' form-horizontal';
8079         }
8080
8081
8082         return cfg;
8083     },
8084     initEvents : function()
8085     {
8086         this.el.on('submit', this.onSubmit, this);
8087         // this was added as random key presses on the form where triggering form submit.
8088         this.el.on('keypress', function(e) {
8089             if (e.getCharCode() != 13) {
8090                 return true;
8091             }
8092             // we might need to allow it for textareas.. and some other items.
8093             // check e.getTarget().
8094
8095             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8096                 return true;
8097             }
8098
8099             Roo.log("keypress blocked");
8100
8101             e.preventDefault();
8102             return false;
8103         });
8104         
8105     },
8106     // private
8107     onSubmit : function(e){
8108         e.stopEvent();
8109     },
8110
8111      /**
8112      * Returns true if client-side validation on the form is successful.
8113      * @return Boolean
8114      */
8115     isValid : function(){
8116         var items = this.getItems();
8117         var valid = true;
8118         var target = false;
8119         
8120         items.each(function(f){
8121             
8122             if(f.validate()){
8123                 return;
8124             }
8125             
8126             Roo.log('invalid field: ' + f.name);
8127             
8128             valid = false;
8129
8130             if(!target && f.el.isVisible(true)){
8131                 target = f;
8132             }
8133            
8134         });
8135         
8136         if(this.errorMask && !valid){
8137             Roo.bootstrap.Form.popover.mask(this, target);
8138         }
8139         
8140         return valid;
8141     },
8142     
8143     /**
8144      * Returns true if any fields in this form have changed since their original load.
8145      * @return Boolean
8146      */
8147     isDirty : function(){
8148         var dirty = false;
8149         var items = this.getItems();
8150         items.each(function(f){
8151            if(f.isDirty()){
8152                dirty = true;
8153                return false;
8154            }
8155            return true;
8156         });
8157         return dirty;
8158     },
8159      /**
8160      * Performs a predefined action (submit or load) or custom actions you define on this form.
8161      * @param {String} actionName The name of the action type
8162      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8163      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8164      * accept other config options):
8165      * <pre>
8166 Property          Type             Description
8167 ----------------  ---------------  ----------------------------------------------------------------------------------
8168 url               String           The url for the action (defaults to the form's url)
8169 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8170 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8171 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8172                                    validate the form on the client (defaults to false)
8173      * </pre>
8174      * @return {BasicForm} this
8175      */
8176     doAction : function(action, options){
8177         if(typeof action == 'string'){
8178             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8179         }
8180         if(this.fireEvent('beforeaction', this, action) !== false){
8181             this.beforeAction(action);
8182             action.run.defer(100, action);
8183         }
8184         return this;
8185     },
8186
8187     // private
8188     beforeAction : function(action){
8189         var o = action.options;
8190         
8191         if(this.loadMask){
8192             
8193             if(this.maskBody){
8194                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8195             } else {
8196                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8197             }
8198         }
8199         // not really supported yet.. ??
8200
8201         //if(this.waitMsgTarget === true){
8202         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8203         //}else if(this.waitMsgTarget){
8204         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8205         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8206         //}else {
8207         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8208        // }
8209
8210     },
8211
8212     // private
8213     afterAction : function(action, success){
8214         this.activeAction = null;
8215         var o = action.options;
8216
8217         if(this.loadMask){
8218             
8219             if(this.maskBody){
8220                 Roo.get(document.body).unmask();
8221             } else {
8222                 this.el.unmask();
8223             }
8224         }
8225         
8226         //if(this.waitMsgTarget === true){
8227 //            this.el.unmask();
8228         //}else if(this.waitMsgTarget){
8229         //    this.waitMsgTarget.unmask();
8230         //}else{
8231         //    Roo.MessageBox.updateProgress(1);
8232         //    Roo.MessageBox.hide();
8233        // }
8234         //
8235         if(success){
8236             if(o.reset){
8237                 this.reset();
8238             }
8239             Roo.callback(o.success, o.scope, [this, action]);
8240             this.fireEvent('actioncomplete', this, action);
8241
8242         }else{
8243
8244             // failure condition..
8245             // we have a scenario where updates need confirming.
8246             // eg. if a locking scenario exists..
8247             // we look for { errors : { needs_confirm : true }} in the response.
8248             if (
8249                 (typeof(action.result) != 'undefined')  &&
8250                 (typeof(action.result.errors) != 'undefined')  &&
8251                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8252            ){
8253                 var _t = this;
8254                 Roo.log("not supported yet");
8255                  /*
8256
8257                 Roo.MessageBox.confirm(
8258                     "Change requires confirmation",
8259                     action.result.errorMsg,
8260                     function(r) {
8261                         if (r != 'yes') {
8262                             return;
8263                         }
8264                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8265                     }
8266
8267                 );
8268                 */
8269
8270
8271                 return;
8272             }
8273
8274             Roo.callback(o.failure, o.scope, [this, action]);
8275             // show an error message if no failed handler is set..
8276             if (!this.hasListener('actionfailed')) {
8277                 Roo.log("need to add dialog support");
8278                 /*
8279                 Roo.MessageBox.alert("Error",
8280                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8281                         action.result.errorMsg :
8282                         "Saving Failed, please check your entries or try again"
8283                 );
8284                 */
8285             }
8286
8287             this.fireEvent('actionfailed', this, action);
8288         }
8289
8290     },
8291     /**
8292      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8293      * @param {String} id The value to search for
8294      * @return Field
8295      */
8296     findField : function(id){
8297         var items = this.getItems();
8298         var field = items.get(id);
8299         if(!field){
8300              items.each(function(f){
8301                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8302                     field = f;
8303                     return false;
8304                 }
8305                 return true;
8306             });
8307         }
8308         return field || null;
8309     },
8310      /**
8311      * Mark fields in this form invalid in bulk.
8312      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8313      * @return {BasicForm} this
8314      */
8315     markInvalid : function(errors){
8316         if(errors instanceof Array){
8317             for(var i = 0, len = errors.length; i < len; i++){
8318                 var fieldError = errors[i];
8319                 var f = this.findField(fieldError.id);
8320                 if(f){
8321                     f.markInvalid(fieldError.msg);
8322                 }
8323             }
8324         }else{
8325             var field, id;
8326             for(id in errors){
8327                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8328                     field.markInvalid(errors[id]);
8329                 }
8330             }
8331         }
8332         //Roo.each(this.childForms || [], function (f) {
8333         //    f.markInvalid(errors);
8334         //});
8335
8336         return this;
8337     },
8338
8339     /**
8340      * Set values for fields in this form in bulk.
8341      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8342      * @return {BasicForm} this
8343      */
8344     setValues : function(values){
8345         if(values instanceof Array){ // array of objects
8346             for(var i = 0, len = values.length; i < len; i++){
8347                 var v = values[i];
8348                 var f = this.findField(v.id);
8349                 if(f){
8350                     f.setValue(v.value);
8351                     if(this.trackResetOnLoad){
8352                         f.originalValue = f.getValue();
8353                     }
8354                 }
8355             }
8356         }else{ // object hash
8357             var field, id;
8358             for(id in values){
8359                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8360
8361                     if (field.setFromData &&
8362                         field.valueField &&
8363                         field.displayField &&
8364                         // combos' with local stores can
8365                         // be queried via setValue()
8366                         // to set their value..
8367                         (field.store && !field.store.isLocal)
8368                         ) {
8369                         // it's a combo
8370                         var sd = { };
8371                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8372                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8373                         field.setFromData(sd);
8374
8375                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8376                         
8377                         field.setFromData(values);
8378                         
8379                     } else {
8380                         field.setValue(values[id]);
8381                     }
8382
8383
8384                     if(this.trackResetOnLoad){
8385                         field.originalValue = field.getValue();
8386                     }
8387                 }
8388             }
8389         }
8390
8391         //Roo.each(this.childForms || [], function (f) {
8392         //    f.setValues(values);
8393         //});
8394
8395         return this;
8396     },
8397
8398     /**
8399      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8400      * they are returned as an array.
8401      * @param {Boolean} asString
8402      * @return {Object}
8403      */
8404     getValues : function(asString){
8405         //if (this.childForms) {
8406             // copy values from the child forms
8407         //    Roo.each(this.childForms, function (f) {
8408         //        this.setValues(f.getValues());
8409         //    }, this);
8410         //}
8411
8412
8413
8414         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8415         if(asString === true){
8416             return fs;
8417         }
8418         return Roo.urlDecode(fs);
8419     },
8420
8421     /**
8422      * Returns the fields in this form as an object with key/value pairs.
8423      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8424      * @return {Object}
8425      */
8426     getFieldValues : function(with_hidden)
8427     {
8428         var items = this.getItems();
8429         var ret = {};
8430         items.each(function(f){
8431             
8432             if (!f.getName()) {
8433                 return;
8434             }
8435             
8436             var v = f.getValue();
8437             
8438             if (f.inputType =='radio') {
8439                 if (typeof(ret[f.getName()]) == 'undefined') {
8440                     ret[f.getName()] = ''; // empty..
8441                 }
8442
8443                 if (!f.el.dom.checked) {
8444                     return;
8445
8446                 }
8447                 v = f.el.dom.value;
8448
8449             }
8450             
8451             if(f.xtype == 'MoneyField'){
8452                 ret[f.currencyName] = f.getCurrency();
8453             }
8454
8455             // not sure if this supported any more..
8456             if ((typeof(v) == 'object') && f.getRawValue) {
8457                 v = f.getRawValue() ; // dates..
8458             }
8459             // combo boxes where name != hiddenName...
8460             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8461                 ret[f.name] = f.getRawValue();
8462             }
8463             ret[f.getName()] = v;
8464         });
8465
8466         return ret;
8467     },
8468
8469     /**
8470      * Clears all invalid messages in this form.
8471      * @return {BasicForm} this
8472      */
8473     clearInvalid : function(){
8474         var items = this.getItems();
8475
8476         items.each(function(f){
8477            f.clearInvalid();
8478         });
8479
8480         return this;
8481     },
8482
8483     /**
8484      * Resets this form.
8485      * @return {BasicForm} this
8486      */
8487     reset : function(){
8488         var items = this.getItems();
8489         items.each(function(f){
8490             f.reset();
8491         });
8492
8493         Roo.each(this.childForms || [], function (f) {
8494             f.reset();
8495         });
8496
8497
8498         return this;
8499     },
8500     
8501     getItems : function()
8502     {
8503         var r=new Roo.util.MixedCollection(false, function(o){
8504             return o.id || (o.id = Roo.id());
8505         });
8506         var iter = function(el) {
8507             if (el.inputEl) {
8508                 r.add(el);
8509             }
8510             if (!el.items) {
8511                 return;
8512             }
8513             Roo.each(el.items,function(e) {
8514                 iter(e);
8515             });
8516         };
8517
8518         iter(this);
8519         return r;
8520     },
8521     
8522     hideFields : function(items)
8523     {
8524         Roo.each(items, function(i){
8525             
8526             var f = this.findField(i);
8527             
8528             if(!f){
8529                 return;
8530             }
8531             
8532             f.hide();
8533             
8534         }, this);
8535     },
8536     
8537     showFields : function(items)
8538     {
8539         Roo.each(items, function(i){
8540             
8541             var f = this.findField(i);
8542             
8543             if(!f){
8544                 return;
8545             }
8546             
8547             f.show();
8548             
8549         }, this);
8550     }
8551
8552 });
8553
8554 Roo.apply(Roo.bootstrap.Form, {
8555     
8556     popover : {
8557         
8558         padding : 5,
8559         
8560         isApplied : false,
8561         
8562         isMasked : false,
8563         
8564         form : false,
8565         
8566         target : false,
8567         
8568         toolTip : false,
8569         
8570         intervalID : false,
8571         
8572         maskEl : false,
8573         
8574         apply : function()
8575         {
8576             if(this.isApplied){
8577                 return;
8578             }
8579             
8580             this.maskEl = {
8581                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8582                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8583                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8584                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8585             };
8586             
8587             this.maskEl.top.enableDisplayMode("block");
8588             this.maskEl.left.enableDisplayMode("block");
8589             this.maskEl.bottom.enableDisplayMode("block");
8590             this.maskEl.right.enableDisplayMode("block");
8591             
8592             this.toolTip = new Roo.bootstrap.Tooltip({
8593                 cls : 'roo-form-error-popover',
8594                 alignment : {
8595                     'left' : ['r-l', [-2,0], 'right'],
8596                     'right' : ['l-r', [2,0], 'left'],
8597                     'bottom' : ['tl-bl', [0,2], 'top'],
8598                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8599                 }
8600             });
8601             
8602             this.toolTip.render(Roo.get(document.body));
8603
8604             this.toolTip.el.enableDisplayMode("block");
8605             
8606             Roo.get(document.body).on('click', function(){
8607                 this.unmask();
8608             }, this);
8609             
8610             Roo.get(document.body).on('touchstart', function(){
8611                 this.unmask();
8612             }, this);
8613             
8614             this.isApplied = true
8615         },
8616         
8617         mask : function(form, target)
8618         {
8619             this.form = form;
8620             
8621             this.target = target;
8622             
8623             if(!this.form.errorMask || !target.el){
8624                 return;
8625             }
8626             
8627             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8628             
8629             Roo.log(scrollable);
8630             
8631             var ot = this.target.el.calcOffsetsTo(scrollable);
8632             
8633             var scrollTo = ot[1] - this.form.maskOffset;
8634             
8635             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8636             
8637             scrollable.scrollTo('top', scrollTo);
8638             
8639             var box = this.target.el.getBox();
8640             Roo.log(box);
8641             var zIndex = Roo.bootstrap.Modal.zIndex++;
8642
8643             
8644             this.maskEl.top.setStyle('position', 'absolute');
8645             this.maskEl.top.setStyle('z-index', zIndex);
8646             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8647             this.maskEl.top.setLeft(0);
8648             this.maskEl.top.setTop(0);
8649             this.maskEl.top.show();
8650             
8651             this.maskEl.left.setStyle('position', 'absolute');
8652             this.maskEl.left.setStyle('z-index', zIndex);
8653             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8654             this.maskEl.left.setLeft(0);
8655             this.maskEl.left.setTop(box.y - this.padding);
8656             this.maskEl.left.show();
8657
8658             this.maskEl.bottom.setStyle('position', 'absolute');
8659             this.maskEl.bottom.setStyle('z-index', zIndex);
8660             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8661             this.maskEl.bottom.setLeft(0);
8662             this.maskEl.bottom.setTop(box.bottom + this.padding);
8663             this.maskEl.bottom.show();
8664
8665             this.maskEl.right.setStyle('position', 'absolute');
8666             this.maskEl.right.setStyle('z-index', zIndex);
8667             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8668             this.maskEl.right.setLeft(box.right + this.padding);
8669             this.maskEl.right.setTop(box.y - this.padding);
8670             this.maskEl.right.show();
8671
8672             this.toolTip.bindEl = this.target.el;
8673
8674             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8675
8676             var tip = this.target.blankText;
8677
8678             if(this.target.getValue() !== '' ) {
8679                 
8680                 if (this.target.invalidText.length) {
8681                     tip = this.target.invalidText;
8682                 } else if (this.target.regexText.length){
8683                     tip = this.target.regexText;
8684                 }
8685             }
8686
8687             this.toolTip.show(tip);
8688
8689             this.intervalID = window.setInterval(function() {
8690                 Roo.bootstrap.Form.popover.unmask();
8691             }, 10000);
8692
8693             window.onwheel = function(){ return false;};
8694             
8695             (function(){ this.isMasked = true; }).defer(500, this);
8696             
8697         },
8698         
8699         unmask : function()
8700         {
8701             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8702                 return;
8703             }
8704             
8705             this.maskEl.top.setStyle('position', 'absolute');
8706             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8707             this.maskEl.top.hide();
8708
8709             this.maskEl.left.setStyle('position', 'absolute');
8710             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8711             this.maskEl.left.hide();
8712
8713             this.maskEl.bottom.setStyle('position', 'absolute');
8714             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8715             this.maskEl.bottom.hide();
8716
8717             this.maskEl.right.setStyle('position', 'absolute');
8718             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8719             this.maskEl.right.hide();
8720             
8721             this.toolTip.hide();
8722             
8723             this.toolTip.el.hide();
8724             
8725             window.onwheel = function(){ return true;};
8726             
8727             if(this.intervalID){
8728                 window.clearInterval(this.intervalID);
8729                 this.intervalID = false;
8730             }
8731             
8732             this.isMasked = false;
8733             
8734         }
8735         
8736     }
8737     
8738 });
8739
8740 /*
8741  * Based on:
8742  * Ext JS Library 1.1.1
8743  * Copyright(c) 2006-2007, Ext JS, LLC.
8744  *
8745  * Originally Released Under LGPL - original licence link has changed is not relivant.
8746  *
8747  * Fork - LGPL
8748  * <script type="text/javascript">
8749  */
8750 /**
8751  * @class Roo.form.VTypes
8752  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8753  * @singleton
8754  */
8755 Roo.form.VTypes = function(){
8756     // closure these in so they are only created once.
8757     var alpha = /^[a-zA-Z_]+$/;
8758     var alphanum = /^[a-zA-Z0-9_]+$/;
8759     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8760     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8761
8762     // All these messages and functions are configurable
8763     return {
8764         /**
8765          * The function used to validate email addresses
8766          * @param {String} value The email address
8767          */
8768         'email' : function(v){
8769             return email.test(v);
8770         },
8771         /**
8772          * The error text to display when the email validation function returns false
8773          * @type String
8774          */
8775         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8776         /**
8777          * The keystroke filter mask to be applied on email input
8778          * @type RegExp
8779          */
8780         'emailMask' : /[a-z0-9_\.\-@]/i,
8781
8782         /**
8783          * The function used to validate URLs
8784          * @param {String} value The URL
8785          */
8786         'url' : function(v){
8787             return url.test(v);
8788         },
8789         /**
8790          * The error text to display when the url validation function returns false
8791          * @type String
8792          */
8793         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8794         
8795         /**
8796          * The function used to validate alpha values
8797          * @param {String} value The value
8798          */
8799         'alpha' : function(v){
8800             return alpha.test(v);
8801         },
8802         /**
8803          * The error text to display when the alpha validation function returns false
8804          * @type String
8805          */
8806         'alphaText' : 'This field should only contain letters and _',
8807         /**
8808          * The keystroke filter mask to be applied on alpha input
8809          * @type RegExp
8810          */
8811         'alphaMask' : /[a-z_]/i,
8812
8813         /**
8814          * The function used to validate alphanumeric values
8815          * @param {String} value The value
8816          */
8817         'alphanum' : function(v){
8818             return alphanum.test(v);
8819         },
8820         /**
8821          * The error text to display when the alphanumeric validation function returns false
8822          * @type String
8823          */
8824         'alphanumText' : 'This field should only contain letters, numbers and _',
8825         /**
8826          * The keystroke filter mask to be applied on alphanumeric input
8827          * @type RegExp
8828          */
8829         'alphanumMask' : /[a-z0-9_]/i
8830     };
8831 }();/*
8832  * - LGPL
8833  *
8834  * Input
8835  * 
8836  */
8837
8838 /**
8839  * @class Roo.bootstrap.Input
8840  * @extends Roo.bootstrap.Component
8841  * Bootstrap Input class
8842  * @cfg {Boolean} disabled is it disabled
8843  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8844  * @cfg {String} name name of the input
8845  * @cfg {string} fieldLabel - the label associated
8846  * @cfg {string} placeholder - placeholder to put in text.
8847  * @cfg {string}  before - input group add on before
8848  * @cfg {string} after - input group add on after
8849  * @cfg {string} size - (lg|sm) or leave empty..
8850  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8851  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8852  * @cfg {Number} md colspan out of 12 for computer-sized screens
8853  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8854  * @cfg {string} value default value of the input
8855  * @cfg {Number} labelWidth set the width of label 
8856  * @cfg {Number} labellg set the width of label (1-12)
8857  * @cfg {Number} labelmd set the width of label (1-12)
8858  * @cfg {Number} labelsm set the width of label (1-12)
8859  * @cfg {Number} labelxs set the width of label (1-12)
8860  * @cfg {String} labelAlign (top|left)
8861  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8862  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8863  * @cfg {String} indicatorpos (left|right) default left
8864  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8865  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8866
8867  * @cfg {String} align (left|center|right) Default left
8868  * @cfg {Boolean} forceFeedback (true|false) Default false
8869  * 
8870  * @constructor
8871  * Create a new Input
8872  * @param {Object} config The config object
8873  */
8874
8875 Roo.bootstrap.Input = function(config){
8876     
8877     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8878     
8879     this.addEvents({
8880         /**
8881          * @event focus
8882          * Fires when this field receives input focus.
8883          * @param {Roo.form.Field} this
8884          */
8885         focus : true,
8886         /**
8887          * @event blur
8888          * Fires when this field loses input focus.
8889          * @param {Roo.form.Field} this
8890          */
8891         blur : true,
8892         /**
8893          * @event specialkey
8894          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8895          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8896          * @param {Roo.form.Field} this
8897          * @param {Roo.EventObject} e The event object
8898          */
8899         specialkey : true,
8900         /**
8901          * @event change
8902          * Fires just before the field blurs if the field value has changed.
8903          * @param {Roo.form.Field} this
8904          * @param {Mixed} newValue The new value
8905          * @param {Mixed} oldValue The original value
8906          */
8907         change : true,
8908         /**
8909          * @event invalid
8910          * Fires after the field has been marked as invalid.
8911          * @param {Roo.form.Field} this
8912          * @param {String} msg The validation message
8913          */
8914         invalid : true,
8915         /**
8916          * @event valid
8917          * Fires after the field has been validated with no errors.
8918          * @param {Roo.form.Field} this
8919          */
8920         valid : true,
8921          /**
8922          * @event keyup
8923          * Fires after the key up
8924          * @param {Roo.form.Field} this
8925          * @param {Roo.EventObject}  e The event Object
8926          */
8927         keyup : true
8928     });
8929 };
8930
8931 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8932      /**
8933      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8934       automatic validation (defaults to "keyup").
8935      */
8936     validationEvent : "keyup",
8937      /**
8938      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8939      */
8940     validateOnBlur : true,
8941     /**
8942      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8943      */
8944     validationDelay : 250,
8945      /**
8946      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8947      */
8948     focusClass : "x-form-focus",  // not needed???
8949     
8950        
8951     /**
8952      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8953      */
8954     invalidClass : "has-warning",
8955     
8956     /**
8957      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8958      */
8959     validClass : "has-success",
8960     
8961     /**
8962      * @cfg {Boolean} hasFeedback (true|false) default true
8963      */
8964     hasFeedback : true,
8965     
8966     /**
8967      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8968      */
8969     invalidFeedbackClass : "glyphicon-warning-sign",
8970     
8971     /**
8972      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8973      */
8974     validFeedbackClass : "glyphicon-ok",
8975     
8976     /**
8977      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8978      */
8979     selectOnFocus : false,
8980     
8981      /**
8982      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8983      */
8984     maskRe : null,
8985        /**
8986      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8987      */
8988     vtype : null,
8989     
8990       /**
8991      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8992      */
8993     disableKeyFilter : false,
8994     
8995        /**
8996      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8997      */
8998     disabled : false,
8999      /**
9000      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9001      */
9002     allowBlank : true,
9003     /**
9004      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9005      */
9006     blankText : "Please complete this mandatory field",
9007     
9008      /**
9009      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9010      */
9011     minLength : 0,
9012     /**
9013      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9014      */
9015     maxLength : Number.MAX_VALUE,
9016     /**
9017      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9018      */
9019     minLengthText : "The minimum length for this field is {0}",
9020     /**
9021      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9022      */
9023     maxLengthText : "The maximum length for this field is {0}",
9024   
9025     
9026     /**
9027      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9028      * If available, this function will be called only after the basic validators all return true, and will be passed the
9029      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9030      */
9031     validator : null,
9032     /**
9033      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9034      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9035      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9036      */
9037     regex : null,
9038     /**
9039      * @cfg {String} regexText -- Depricated - use Invalid Text
9040      */
9041     regexText : "",
9042     
9043     /**
9044      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9045      */
9046     invalidText : "",
9047     
9048     
9049     
9050     autocomplete: false,
9051     
9052     
9053     fieldLabel : '',
9054     inputType : 'text',
9055     
9056     name : false,
9057     placeholder: false,
9058     before : false,
9059     after : false,
9060     size : false,
9061     hasFocus : false,
9062     preventMark: false,
9063     isFormField : true,
9064     value : '',
9065     labelWidth : 2,
9066     labelAlign : false,
9067     readOnly : false,
9068     align : false,
9069     formatedValue : false,
9070     forceFeedback : false,
9071     
9072     indicatorpos : 'left',
9073     
9074     labellg : 0,
9075     labelmd : 0,
9076     labelsm : 0,
9077     labelxs : 0,
9078     
9079     capture : '',
9080     accept : '',
9081     
9082     parentLabelAlign : function()
9083     {
9084         var parent = this;
9085         while (parent.parent()) {
9086             parent = parent.parent();
9087             if (typeof(parent.labelAlign) !='undefined') {
9088                 return parent.labelAlign;
9089             }
9090         }
9091         return 'left';
9092         
9093     },
9094     
9095     getAutoCreate : function()
9096     {
9097         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9098         
9099         var id = Roo.id();
9100         
9101         var cfg = {};
9102         
9103         if(this.inputType != 'hidden'){
9104             cfg.cls = 'form-group' //input-group
9105         }
9106         
9107         var input =  {
9108             tag: 'input',
9109             id : id,
9110             type : this.inputType,
9111             value : this.value,
9112             cls : 'form-control',
9113             placeholder : this.placeholder || '',
9114             autocomplete : this.autocomplete || 'new-password'
9115         };
9116         
9117         if(this.capture.length){
9118             input.capture = this.capture;
9119         }
9120         
9121         if(this.accept.length){
9122             input.accept = this.accept + "/*";
9123         }
9124         
9125         if(this.align){
9126             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9127         }
9128         
9129         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9130             input.maxLength = this.maxLength;
9131         }
9132         
9133         if (this.disabled) {
9134             input.disabled=true;
9135         }
9136         
9137         if (this.readOnly) {
9138             input.readonly=true;
9139         }
9140         
9141         if (this.name) {
9142             input.name = this.name;
9143         }
9144         
9145         if (this.size) {
9146             input.cls += ' input-' + this.size;
9147         }
9148         
9149         var settings=this;
9150         ['xs','sm','md','lg'].map(function(size){
9151             if (settings[size]) {
9152                 cfg.cls += ' col-' + size + '-' + settings[size];
9153             }
9154         });
9155         
9156         var inputblock = input;
9157         
9158         var feedback = {
9159             tag: 'span',
9160             cls: 'glyphicon form-control-feedback'
9161         };
9162             
9163         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9164             
9165             inputblock = {
9166                 cls : 'has-feedback',
9167                 cn :  [
9168                     input,
9169                     feedback
9170                 ] 
9171             };  
9172         }
9173         
9174         if (this.before || this.after) {
9175             
9176             inputblock = {
9177                 cls : 'input-group',
9178                 cn :  [] 
9179             };
9180             
9181             if (this.before && typeof(this.before) == 'string') {
9182                 
9183                 inputblock.cn.push({
9184                     tag :'span',
9185                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9186                     html : this.before
9187                 });
9188             }
9189             if (this.before && typeof(this.before) == 'object') {
9190                 this.before = Roo.factory(this.before);
9191                 
9192                 inputblock.cn.push({
9193                     tag :'span',
9194                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9195                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9196                 });
9197             }
9198             
9199             inputblock.cn.push(input);
9200             
9201             if (this.after && typeof(this.after) == 'string') {
9202                 inputblock.cn.push({
9203                     tag :'span',
9204                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9205                     html : this.after
9206                 });
9207             }
9208             if (this.after && typeof(this.after) == 'object') {
9209                 this.after = Roo.factory(this.after);
9210                 
9211                 inputblock.cn.push({
9212                     tag :'span',
9213                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9214                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9215                 });
9216             }
9217             
9218             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9219                 inputblock.cls += ' has-feedback';
9220                 inputblock.cn.push(feedback);
9221             }
9222         };
9223         var indicator = {
9224             tag : 'i',
9225             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9226             tooltip : 'This field is required'
9227         };
9228         if (Roo.bootstrap.version == 4) {
9229             indicator = {
9230                 tag : 'i',
9231                 style : 'display-none'
9232             };
9233         }
9234         if (align ==='left' && this.fieldLabel.length) {
9235             
9236             cfg.cls += ' roo-form-group-label-left row';
9237             
9238             cfg.cn = [
9239                 indicator,
9240                 {
9241                     tag: 'label',
9242                     'for' :  id,
9243                     cls : 'control-label col-form-label',
9244                     html : this.fieldLabel
9245
9246                 },
9247                 {
9248                     cls : "", 
9249                     cn: [
9250                         inputblock
9251                     ]
9252                 }
9253             ];
9254             
9255             var labelCfg = cfg.cn[1];
9256             var contentCfg = cfg.cn[2];
9257             
9258             if(this.indicatorpos == 'right'){
9259                 cfg.cn = [
9260                     {
9261                         tag: 'label',
9262                         'for' :  id,
9263                         cls : 'control-label col-form-label',
9264                         cn : [
9265                             {
9266                                 tag : 'span',
9267                                 html : this.fieldLabel
9268                             },
9269                             indicator
9270                         ]
9271                     },
9272                     {
9273                         cls : "",
9274                         cn: [
9275                             inputblock
9276                         ]
9277                     }
9278
9279                 ];
9280                 
9281                 labelCfg = cfg.cn[0];
9282                 contentCfg = cfg.cn[1];
9283             
9284             }
9285             
9286             if(this.labelWidth > 12){
9287                 labelCfg.style = "width: " + this.labelWidth + 'px';
9288             }
9289             
9290             if(this.labelWidth < 13 && this.labelmd == 0){
9291                 this.labelmd = this.labelWidth;
9292             }
9293             
9294             if(this.labellg > 0){
9295                 labelCfg.cls += ' col-lg-' + this.labellg;
9296                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9297             }
9298             
9299             if(this.labelmd > 0){
9300                 labelCfg.cls += ' col-md-' + this.labelmd;
9301                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9302             }
9303             
9304             if(this.labelsm > 0){
9305                 labelCfg.cls += ' col-sm-' + this.labelsm;
9306                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9307             }
9308             
9309             if(this.labelxs > 0){
9310                 labelCfg.cls += ' col-xs-' + this.labelxs;
9311                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9312             }
9313             
9314             
9315         } else if ( this.fieldLabel.length) {
9316                 
9317             cfg.cn = [
9318                 {
9319                     tag : 'i',
9320                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9321                     tooltip : 'This field is required'
9322                 },
9323                 {
9324                     tag: 'label',
9325                    //cls : 'input-group-addon',
9326                     html : this.fieldLabel
9327
9328                 },
9329
9330                inputblock
9331
9332            ];
9333            
9334            if(this.indicatorpos == 'right'){
9335                 
9336                 cfg.cn = [
9337                     {
9338                         tag: 'label',
9339                        //cls : 'input-group-addon',
9340                         html : this.fieldLabel
9341
9342                     },
9343                     {
9344                         tag : 'i',
9345                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9346                         tooltip : 'This field is required'
9347                     },
9348
9349                    inputblock
9350
9351                ];
9352
9353             }
9354
9355         } else {
9356             
9357             cfg.cn = [
9358
9359                     inputblock
9360
9361             ];
9362                 
9363                 
9364         };
9365         
9366         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9367            cfg.cls += ' navbar-form';
9368         }
9369         
9370         if (this.parentType === 'NavGroup') {
9371            cfg.cls += ' navbar-form';
9372            cfg.tag = 'li';
9373         }
9374         
9375         return cfg;
9376         
9377     },
9378     /**
9379      * return the real input element.
9380      */
9381     inputEl: function ()
9382     {
9383         return this.el.select('input.form-control',true).first();
9384     },
9385     
9386     tooltipEl : function()
9387     {
9388         return this.inputEl();
9389     },
9390     
9391     indicatorEl : function()
9392     {
9393         if (Roo.bootstrap.version == 4) {
9394             return false; // not enabled in v4 yet.
9395         }
9396         
9397         var indicator = this.el.select('i.roo-required-indicator',true).first();
9398         
9399         if(!indicator){
9400             return false;
9401         }
9402         
9403         return indicator;
9404         
9405     },
9406     
9407     setDisabled : function(v)
9408     {
9409         var i  = this.inputEl().dom;
9410         if (!v) {
9411             i.removeAttribute('disabled');
9412             return;
9413             
9414         }
9415         i.setAttribute('disabled','true');
9416     },
9417     initEvents : function()
9418     {
9419           
9420         this.inputEl().on("keydown" , this.fireKey,  this);
9421         this.inputEl().on("focus", this.onFocus,  this);
9422         this.inputEl().on("blur", this.onBlur,  this);
9423         
9424         this.inputEl().relayEvent('keyup', this);
9425         
9426         this.indicator = this.indicatorEl();
9427         
9428         if(this.indicator){
9429             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9430         }
9431  
9432         // reference to original value for reset
9433         this.originalValue = this.getValue();
9434         //Roo.form.TextField.superclass.initEvents.call(this);
9435         if(this.validationEvent == 'keyup'){
9436             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9437             this.inputEl().on('keyup', this.filterValidation, this);
9438         }
9439         else if(this.validationEvent !== false){
9440             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9441         }
9442         
9443         if(this.selectOnFocus){
9444             this.on("focus", this.preFocus, this);
9445             
9446         }
9447         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9448             this.inputEl().on("keypress", this.filterKeys, this);
9449         } else {
9450             this.inputEl().relayEvent('keypress', this);
9451         }
9452        /* if(this.grow){
9453             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9454             this.el.on("click", this.autoSize,  this);
9455         }
9456         */
9457         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9458             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9459         }
9460         
9461         if (typeof(this.before) == 'object') {
9462             this.before.render(this.el.select('.roo-input-before',true).first());
9463         }
9464         if (typeof(this.after) == 'object') {
9465             this.after.render(this.el.select('.roo-input-after',true).first());
9466         }
9467         
9468         this.inputEl().on('change', this.onChange, this);
9469         
9470     },
9471     filterValidation : function(e){
9472         if(!e.isNavKeyPress()){
9473             this.validationTask.delay(this.validationDelay);
9474         }
9475     },
9476      /**
9477      * Validates the field value
9478      * @return {Boolean} True if the value is valid, else false
9479      */
9480     validate : function(){
9481         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9482         if(this.disabled || this.validateValue(this.getRawValue())){
9483             this.markValid();
9484             return true;
9485         }
9486         
9487         this.markInvalid();
9488         return false;
9489     },
9490     
9491     
9492     /**
9493      * Validates a value according to the field's validation rules and marks the field as invalid
9494      * if the validation fails
9495      * @param {Mixed} value The value to validate
9496      * @return {Boolean} True if the value is valid, else false
9497      */
9498     validateValue : function(value)
9499     {
9500         if(this.getVisibilityEl().hasClass('hidden')){
9501             return true;
9502         }
9503         
9504         if(value.length < 1)  { // if it's blank
9505             if(this.allowBlank){
9506                 return true;
9507             }
9508             return false;
9509         }
9510         
9511         if(value.length < this.minLength){
9512             return false;
9513         }
9514         if(value.length > this.maxLength){
9515             return false;
9516         }
9517         if(this.vtype){
9518             var vt = Roo.form.VTypes;
9519             if(!vt[this.vtype](value, this)){
9520                 return false;
9521             }
9522         }
9523         if(typeof this.validator == "function"){
9524             var msg = this.validator(value);
9525             if(msg !== true){
9526                 return false;
9527             }
9528             if (typeof(msg) == 'string') {
9529                 this.invalidText = msg;
9530             }
9531         }
9532         
9533         if(this.regex && !this.regex.test(value)){
9534             return false;
9535         }
9536         
9537         return true;
9538     },
9539     
9540      // private
9541     fireKey : function(e){
9542         //Roo.log('field ' + e.getKey());
9543         if(e.isNavKeyPress()){
9544             this.fireEvent("specialkey", this, e);
9545         }
9546     },
9547     focus : function (selectText){
9548         if(this.rendered){
9549             this.inputEl().focus();
9550             if(selectText === true){
9551                 this.inputEl().dom.select();
9552             }
9553         }
9554         return this;
9555     } ,
9556     
9557     onFocus : function(){
9558         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9559            // this.el.addClass(this.focusClass);
9560         }
9561         if(!this.hasFocus){
9562             this.hasFocus = true;
9563             this.startValue = this.getValue();
9564             this.fireEvent("focus", this);
9565         }
9566     },
9567     
9568     beforeBlur : Roo.emptyFn,
9569
9570     
9571     // private
9572     onBlur : function(){
9573         this.beforeBlur();
9574         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9575             //this.el.removeClass(this.focusClass);
9576         }
9577         this.hasFocus = false;
9578         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9579             this.validate();
9580         }
9581         var v = this.getValue();
9582         if(String(v) !== String(this.startValue)){
9583             this.fireEvent('change', this, v, this.startValue);
9584         }
9585         this.fireEvent("blur", this);
9586     },
9587     
9588     onChange : function(e)
9589     {
9590         var v = this.getValue();
9591         if(String(v) !== String(this.startValue)){
9592             this.fireEvent('change', this, v, this.startValue);
9593         }
9594         
9595     },
9596     
9597     /**
9598      * Resets the current field value to the originally loaded value and clears any validation messages
9599      */
9600     reset : function(){
9601         this.setValue(this.originalValue);
9602         this.validate();
9603     },
9604      /**
9605      * Returns the name of the field
9606      * @return {Mixed} name The name field
9607      */
9608     getName: function(){
9609         return this.name;
9610     },
9611      /**
9612      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9613      * @return {Mixed} value The field value
9614      */
9615     getValue : function(){
9616         
9617         var v = this.inputEl().getValue();
9618         
9619         return v;
9620     },
9621     /**
9622      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9623      * @return {Mixed} value The field value
9624      */
9625     getRawValue : function(){
9626         var v = this.inputEl().getValue();
9627         
9628         return v;
9629     },
9630     
9631     /**
9632      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9633      * @param {Mixed} value The value to set
9634      */
9635     setRawValue : function(v){
9636         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9637     },
9638     
9639     selectText : function(start, end){
9640         var v = this.getRawValue();
9641         if(v.length > 0){
9642             start = start === undefined ? 0 : start;
9643             end = end === undefined ? v.length : end;
9644             var d = this.inputEl().dom;
9645             if(d.setSelectionRange){
9646                 d.setSelectionRange(start, end);
9647             }else if(d.createTextRange){
9648                 var range = d.createTextRange();
9649                 range.moveStart("character", start);
9650                 range.moveEnd("character", v.length-end);
9651                 range.select();
9652             }
9653         }
9654     },
9655     
9656     /**
9657      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9658      * @param {Mixed} value The value to set
9659      */
9660     setValue : function(v){
9661         this.value = v;
9662         if(this.rendered){
9663             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9664             this.validate();
9665         }
9666     },
9667     
9668     /*
9669     processValue : function(value){
9670         if(this.stripCharsRe){
9671             var newValue = value.replace(this.stripCharsRe, '');
9672             if(newValue !== value){
9673                 this.setRawValue(newValue);
9674                 return newValue;
9675             }
9676         }
9677         return value;
9678     },
9679   */
9680     preFocus : function(){
9681         
9682         if(this.selectOnFocus){
9683             this.inputEl().dom.select();
9684         }
9685     },
9686     filterKeys : function(e){
9687         var k = e.getKey();
9688         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9689             return;
9690         }
9691         var c = e.getCharCode(), cc = String.fromCharCode(c);
9692         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9693             return;
9694         }
9695         if(!this.maskRe.test(cc)){
9696             e.stopEvent();
9697         }
9698     },
9699      /**
9700      * Clear any invalid styles/messages for this field
9701      */
9702     clearInvalid : function(){
9703         
9704         if(!this.el || this.preventMark){ // not rendered
9705             return;
9706         }
9707         
9708      
9709         this.el.removeClass(this.invalidClass);
9710         
9711         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9712             
9713             var feedback = this.el.select('.form-control-feedback', true).first();
9714             
9715             if(feedback){
9716                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9717             }
9718             
9719         }
9720         
9721         if(this.indicator){
9722             this.indicator.removeClass('visible');
9723             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9724         }
9725         
9726         this.fireEvent('valid', this);
9727     },
9728     
9729      /**
9730      * Mark this field as valid
9731      */
9732     markValid : function()
9733     {
9734         if(!this.el  || this.preventMark){ // not rendered...
9735             return;
9736         }
9737         
9738         this.el.removeClass([this.invalidClass, this.validClass]);
9739         
9740         var feedback = this.el.select('.form-control-feedback', true).first();
9741             
9742         if(feedback){
9743             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9744         }
9745         
9746         if(this.indicator){
9747             this.indicator.removeClass('visible');
9748             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9749         }
9750         
9751         if(this.disabled){
9752             return;
9753         }
9754         
9755         if(this.allowBlank && !this.getRawValue().length){
9756             return;
9757         }
9758         
9759         this.el.addClass(this.validClass);
9760         
9761         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9762             
9763             var feedback = this.el.select('.form-control-feedback', true).first();
9764             
9765             if(feedback){
9766                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9767                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9768             }
9769             
9770         }
9771         
9772         this.fireEvent('valid', this);
9773     },
9774     
9775      /**
9776      * Mark this field as invalid
9777      * @param {String} msg The validation message
9778      */
9779     markInvalid : function(msg)
9780     {
9781         if(!this.el  || this.preventMark){ // not rendered
9782             return;
9783         }
9784         
9785         this.el.removeClass([this.invalidClass, this.validClass]);
9786         
9787         var feedback = this.el.select('.form-control-feedback', true).first();
9788             
9789         if(feedback){
9790             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9791         }
9792
9793         if(this.disabled){
9794             return;
9795         }
9796         
9797         if(this.allowBlank && !this.getRawValue().length){
9798             return;
9799         }
9800         
9801         if(this.indicator){
9802             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9803             this.indicator.addClass('visible');
9804         }
9805         
9806         this.el.addClass(this.invalidClass);
9807         
9808         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9809             
9810             var feedback = this.el.select('.form-control-feedback', true).first();
9811             
9812             if(feedback){
9813                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9814                 
9815                 if(this.getValue().length || this.forceFeedback){
9816                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9817                 }
9818                 
9819             }
9820             
9821         }
9822         
9823         this.fireEvent('invalid', this, msg);
9824     },
9825     // private
9826     SafariOnKeyDown : function(event)
9827     {
9828         // this is a workaround for a password hang bug on chrome/ webkit.
9829         if (this.inputEl().dom.type != 'password') {
9830             return;
9831         }
9832         
9833         var isSelectAll = false;
9834         
9835         if(this.inputEl().dom.selectionEnd > 0){
9836             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9837         }
9838         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9839             event.preventDefault();
9840             this.setValue('');
9841             return;
9842         }
9843         
9844         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9845             
9846             event.preventDefault();
9847             // this is very hacky as keydown always get's upper case.
9848             //
9849             var cc = String.fromCharCode(event.getCharCode());
9850             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9851             
9852         }
9853     },
9854     adjustWidth : function(tag, w){
9855         tag = tag.toLowerCase();
9856         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9857             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9858                 if(tag == 'input'){
9859                     return w + 2;
9860                 }
9861                 if(tag == 'textarea'){
9862                     return w-2;
9863                 }
9864             }else if(Roo.isOpera){
9865                 if(tag == 'input'){
9866                     return w + 2;
9867                 }
9868                 if(tag == 'textarea'){
9869                     return w-2;
9870                 }
9871             }
9872         }
9873         return w;
9874     },
9875     
9876     setFieldLabel : function(v)
9877     {
9878         if(!this.rendered){
9879             return;
9880         }
9881         
9882         if(this.indicatorEl()){
9883             var ar = this.el.select('label > span',true);
9884             
9885             if (ar.elements.length) {
9886                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9887                 this.fieldLabel = v;
9888                 return;
9889             }
9890             
9891             var br = this.el.select('label',true);
9892             
9893             if(br.elements.length) {
9894                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9895                 this.fieldLabel = v;
9896                 return;
9897             }
9898             
9899             Roo.log('Cannot Found any of label > span || label in input');
9900             return;
9901         }
9902         
9903         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9904         this.fieldLabel = v;
9905         
9906         
9907     }
9908 });
9909
9910  
9911 /*
9912  * - LGPL
9913  *
9914  * Input
9915  * 
9916  */
9917
9918 /**
9919  * @class Roo.bootstrap.TextArea
9920  * @extends Roo.bootstrap.Input
9921  * Bootstrap TextArea class
9922  * @cfg {Number} cols Specifies the visible width of a text area
9923  * @cfg {Number} rows Specifies the visible number of lines in a text area
9924  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9925  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9926  * @cfg {string} html text
9927  * 
9928  * @constructor
9929  * Create a new TextArea
9930  * @param {Object} config The config object
9931  */
9932
9933 Roo.bootstrap.TextArea = function(config){
9934     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9935    
9936 };
9937
9938 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9939      
9940     cols : false,
9941     rows : 5,
9942     readOnly : false,
9943     warp : 'soft',
9944     resize : false,
9945     value: false,
9946     html: false,
9947     
9948     getAutoCreate : function(){
9949         
9950         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9951         
9952         var id = Roo.id();
9953         
9954         var cfg = {};
9955         
9956         if(this.inputType != 'hidden'){
9957             cfg.cls = 'form-group' //input-group
9958         }
9959         
9960         var input =  {
9961             tag: 'textarea',
9962             id : id,
9963             warp : this.warp,
9964             rows : this.rows,
9965             value : this.value || '',
9966             html: this.html || '',
9967             cls : 'form-control',
9968             placeholder : this.placeholder || '' 
9969             
9970         };
9971         
9972         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9973             input.maxLength = this.maxLength;
9974         }
9975         
9976         if(this.resize){
9977             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9978         }
9979         
9980         if(this.cols){
9981             input.cols = this.cols;
9982         }
9983         
9984         if (this.readOnly) {
9985             input.readonly = true;
9986         }
9987         
9988         if (this.name) {
9989             input.name = this.name;
9990         }
9991         
9992         if (this.size) {
9993             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9994         }
9995         
9996         var settings=this;
9997         ['xs','sm','md','lg'].map(function(size){
9998             if (settings[size]) {
9999                 cfg.cls += ' col-' + size + '-' + settings[size];
10000             }
10001         });
10002         
10003         var inputblock = input;
10004         
10005         if(this.hasFeedback && !this.allowBlank){
10006             
10007             var feedback = {
10008                 tag: 'span',
10009                 cls: 'glyphicon form-control-feedback'
10010             };
10011
10012             inputblock = {
10013                 cls : 'has-feedback',
10014                 cn :  [
10015                     input,
10016                     feedback
10017                 ] 
10018             };  
10019         }
10020         
10021         
10022         if (this.before || this.after) {
10023             
10024             inputblock = {
10025                 cls : 'input-group',
10026                 cn :  [] 
10027             };
10028             if (this.before) {
10029                 inputblock.cn.push({
10030                     tag :'span',
10031                     cls : 'input-group-addon',
10032                     html : this.before
10033                 });
10034             }
10035             
10036             inputblock.cn.push(input);
10037             
10038             if(this.hasFeedback && !this.allowBlank){
10039                 inputblock.cls += ' has-feedback';
10040                 inputblock.cn.push(feedback);
10041             }
10042             
10043             if (this.after) {
10044                 inputblock.cn.push({
10045                     tag :'span',
10046                     cls : 'input-group-addon',
10047                     html : this.after
10048                 });
10049             }
10050             
10051         }
10052         
10053         if (align ==='left' && this.fieldLabel.length) {
10054             cfg.cn = [
10055                 {
10056                     tag: 'label',
10057                     'for' :  id,
10058                     cls : 'control-label',
10059                     html : this.fieldLabel
10060                 },
10061                 {
10062                     cls : "",
10063                     cn: [
10064                         inputblock
10065                     ]
10066                 }
10067
10068             ];
10069             
10070             if(this.labelWidth > 12){
10071                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10072             }
10073
10074             if(this.labelWidth < 13 && this.labelmd == 0){
10075                 this.labelmd = this.labelWidth;
10076             }
10077
10078             if(this.labellg > 0){
10079                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10080                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10081             }
10082
10083             if(this.labelmd > 0){
10084                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10085                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10086             }
10087
10088             if(this.labelsm > 0){
10089                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10090                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10091             }
10092
10093             if(this.labelxs > 0){
10094                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10095                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10096             }
10097             
10098         } else if ( this.fieldLabel.length) {
10099             cfg.cn = [
10100
10101                {
10102                    tag: 'label',
10103                    //cls : 'input-group-addon',
10104                    html : this.fieldLabel
10105
10106                },
10107
10108                inputblock
10109
10110            ];
10111
10112         } else {
10113
10114             cfg.cn = [
10115
10116                 inputblock
10117
10118             ];
10119                 
10120         }
10121         
10122         if (this.disabled) {
10123             input.disabled=true;
10124         }
10125         
10126         return cfg;
10127         
10128     },
10129     /**
10130      * return the real textarea element.
10131      */
10132     inputEl: function ()
10133     {
10134         return this.el.select('textarea.form-control',true).first();
10135     },
10136     
10137     /**
10138      * Clear any invalid styles/messages for this field
10139      */
10140     clearInvalid : function()
10141     {
10142         
10143         if(!this.el || this.preventMark){ // not rendered
10144             return;
10145         }
10146         
10147         var label = this.el.select('label', true).first();
10148         var icon = this.el.select('i.fa-star', true).first();
10149         
10150         if(label && icon){
10151             icon.remove();
10152         }
10153         
10154         this.el.removeClass(this.invalidClass);
10155         
10156         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10157             
10158             var feedback = this.el.select('.form-control-feedback', true).first();
10159             
10160             if(feedback){
10161                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10162             }
10163             
10164         }
10165         
10166         this.fireEvent('valid', this);
10167     },
10168     
10169      /**
10170      * Mark this field as valid
10171      */
10172     markValid : function()
10173     {
10174         if(!this.el  || this.preventMark){ // not rendered
10175             return;
10176         }
10177         
10178         this.el.removeClass([this.invalidClass, this.validClass]);
10179         
10180         var feedback = this.el.select('.form-control-feedback', true).first();
10181             
10182         if(feedback){
10183             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10184         }
10185
10186         if(this.disabled || this.allowBlank){
10187             return;
10188         }
10189         
10190         var label = this.el.select('label', true).first();
10191         var icon = this.el.select('i.fa-star', true).first();
10192         
10193         if(label && icon){
10194             icon.remove();
10195         }
10196         
10197         this.el.addClass(this.validClass);
10198         
10199         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10200             
10201             var feedback = this.el.select('.form-control-feedback', true).first();
10202             
10203             if(feedback){
10204                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10205                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10206             }
10207             
10208         }
10209         
10210         this.fireEvent('valid', this);
10211     },
10212     
10213      /**
10214      * Mark this field as invalid
10215      * @param {String} msg The validation message
10216      */
10217     markInvalid : function(msg)
10218     {
10219         if(!this.el  || this.preventMark){ // not rendered
10220             return;
10221         }
10222         
10223         this.el.removeClass([this.invalidClass, this.validClass]);
10224         
10225         var feedback = this.el.select('.form-control-feedback', true).first();
10226             
10227         if(feedback){
10228             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10229         }
10230
10231         if(this.disabled || this.allowBlank){
10232             return;
10233         }
10234         
10235         var label = this.el.select('label', true).first();
10236         var icon = this.el.select('i.fa-star', true).first();
10237         
10238         if(!this.getValue().length && label && !icon){
10239             this.el.createChild({
10240                 tag : 'i',
10241                 cls : 'text-danger fa fa-lg fa-star',
10242                 tooltip : 'This field is required',
10243                 style : 'margin-right:5px;'
10244             }, label, true);
10245         }
10246
10247         this.el.addClass(this.invalidClass);
10248         
10249         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10250             
10251             var feedback = this.el.select('.form-control-feedback', true).first();
10252             
10253             if(feedback){
10254                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10255                 
10256                 if(this.getValue().length || this.forceFeedback){
10257                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10258                 }
10259                 
10260             }
10261             
10262         }
10263         
10264         this.fireEvent('invalid', this, msg);
10265     }
10266 });
10267
10268  
10269 /*
10270  * - LGPL
10271  *
10272  * trigger field - base class for combo..
10273  * 
10274  */
10275  
10276 /**
10277  * @class Roo.bootstrap.TriggerField
10278  * @extends Roo.bootstrap.Input
10279  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10280  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10281  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10282  * for which you can provide a custom implementation.  For example:
10283  * <pre><code>
10284 var trigger = new Roo.bootstrap.TriggerField();
10285 trigger.onTriggerClick = myTriggerFn;
10286 trigger.applyTo('my-field');
10287 </code></pre>
10288  *
10289  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10290  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10291  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10292  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10293  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10294
10295  * @constructor
10296  * Create a new TriggerField.
10297  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10298  * to the base TextField)
10299  */
10300 Roo.bootstrap.TriggerField = function(config){
10301     this.mimicing = false;
10302     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10303 };
10304
10305 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10306     /**
10307      * @cfg {String} triggerClass A CSS class to apply to the trigger
10308      */
10309      /**
10310      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10311      */
10312     hideTrigger:false,
10313
10314     /**
10315      * @cfg {Boolean} removable (true|false) special filter default false
10316      */
10317     removable : false,
10318     
10319     /** @cfg {Boolean} grow @hide */
10320     /** @cfg {Number} growMin @hide */
10321     /** @cfg {Number} growMax @hide */
10322
10323     /**
10324      * @hide 
10325      * @method
10326      */
10327     autoSize: Roo.emptyFn,
10328     // private
10329     monitorTab : true,
10330     // private
10331     deferHeight : true,
10332
10333     
10334     actionMode : 'wrap',
10335     
10336     caret : false,
10337     
10338     
10339     getAutoCreate : function(){
10340        
10341         var align = this.labelAlign || this.parentLabelAlign();
10342         
10343         var id = Roo.id();
10344         
10345         var cfg = {
10346             cls: 'form-group' //input-group
10347         };
10348         
10349         
10350         var input =  {
10351             tag: 'input',
10352             id : id,
10353             type : this.inputType,
10354             cls : 'form-control',
10355             autocomplete: 'new-password',
10356             placeholder : this.placeholder || '' 
10357             
10358         };
10359         if (this.name) {
10360             input.name = this.name;
10361         }
10362         if (this.size) {
10363             input.cls += ' input-' + this.size;
10364         }
10365         
10366         if (this.disabled) {
10367             input.disabled=true;
10368         }
10369         
10370         var inputblock = input;
10371         
10372         if(this.hasFeedback && !this.allowBlank){
10373             
10374             var feedback = {
10375                 tag: 'span',
10376                 cls: 'glyphicon form-control-feedback'
10377             };
10378             
10379             if(this.removable && !this.editable && !this.tickable){
10380                 inputblock = {
10381                     cls : 'has-feedback',
10382                     cn :  [
10383                         inputblock,
10384                         {
10385                             tag: 'button',
10386                             html : 'x',
10387                             cls : 'roo-combo-removable-btn close'
10388                         },
10389                         feedback
10390                     ] 
10391                 };
10392             } else {
10393                 inputblock = {
10394                     cls : 'has-feedback',
10395                     cn :  [
10396                         inputblock,
10397                         feedback
10398                     ] 
10399                 };
10400             }
10401
10402         } else {
10403             if(this.removable && !this.editable && !this.tickable){
10404                 inputblock = {
10405                     cls : 'roo-removable',
10406                     cn :  [
10407                         inputblock,
10408                         {
10409                             tag: 'button',
10410                             html : 'x',
10411                             cls : 'roo-combo-removable-btn close'
10412                         }
10413                     ] 
10414                 };
10415             }
10416         }
10417         
10418         if (this.before || this.after) {
10419             
10420             inputblock = {
10421                 cls : 'input-group',
10422                 cn :  [] 
10423             };
10424             if (this.before) {
10425                 inputblock.cn.push({
10426                     tag :'span',
10427                     cls : 'input-group-addon input-group-prepend input-group-text',
10428                     html : this.before
10429                 });
10430             }
10431             
10432             inputblock.cn.push(input);
10433             
10434             if(this.hasFeedback && !this.allowBlank){
10435                 inputblock.cls += ' has-feedback';
10436                 inputblock.cn.push(feedback);
10437             }
10438             
10439             if (this.after) {
10440                 inputblock.cn.push({
10441                     tag :'span',
10442                     cls : 'input-group-addon input-group-append input-group-text',
10443                     html : this.after
10444                 });
10445             }
10446             
10447         };
10448         
10449       
10450         
10451         var ibwrap = inputblock;
10452         
10453         if(this.multiple){
10454             ibwrap = {
10455                 tag: 'ul',
10456                 cls: 'roo-select2-choices',
10457                 cn:[
10458                     {
10459                         tag: 'li',
10460                         cls: 'roo-select2-search-field',
10461                         cn: [
10462
10463                             inputblock
10464                         ]
10465                     }
10466                 ]
10467             };
10468                 
10469         }
10470         
10471         var combobox = {
10472             cls: 'roo-select2-container input-group',
10473             cn: [
10474                  {
10475                     tag: 'input',
10476                     type : 'hidden',
10477                     cls: 'form-hidden-field'
10478                 },
10479                 ibwrap
10480             ]
10481         };
10482         
10483         if(!this.multiple && this.showToggleBtn){
10484             
10485             var caret = {
10486                         tag: 'span',
10487                         cls: 'caret'
10488              };
10489             if (this.caret != false) {
10490                 caret = {
10491                      tag: 'i',
10492                      cls: 'fa fa-' + this.caret
10493                 };
10494                 
10495             }
10496             
10497             combobox.cn.push({
10498                 tag :'span',
10499                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10500                 cn : [
10501                     caret,
10502                     {
10503                         tag: 'span',
10504                         cls: 'combobox-clear',
10505                         cn  : [
10506                             {
10507                                 tag : 'i',
10508                                 cls: 'icon-remove'
10509                             }
10510                         ]
10511                     }
10512                 ]
10513
10514             })
10515         }
10516         
10517         if(this.multiple){
10518             combobox.cls += ' roo-select2-container-multi';
10519         }
10520          var indicator = {
10521             tag : 'i',
10522             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10523             tooltip : 'This field is required'
10524         };
10525         if (Roo.bootstrap.version == 4) {
10526             indicator = {
10527                 tag : 'i',
10528                 style : 'display:none'
10529             };
10530         }
10531         
10532         
10533         if (align ==='left' && this.fieldLabel.length) {
10534             
10535             cfg.cls += ' roo-form-group-label-left row';
10536
10537             cfg.cn = [
10538                 indicator,
10539                 {
10540                     tag: 'label',
10541                     'for' :  id,
10542                     cls : 'control-label',
10543                     html : this.fieldLabel
10544
10545                 },
10546                 {
10547                     cls : "", 
10548                     cn: [
10549                         combobox
10550                     ]
10551                 }
10552
10553             ];
10554             
10555             var labelCfg = cfg.cn[1];
10556             var contentCfg = cfg.cn[2];
10557             
10558             if(this.indicatorpos == 'right'){
10559                 cfg.cn = [
10560                     {
10561                         tag: 'label',
10562                         'for' :  id,
10563                         cls : 'control-label',
10564                         cn : [
10565                             {
10566                                 tag : 'span',
10567                                 html : this.fieldLabel
10568                             },
10569                             indicator
10570                         ]
10571                     },
10572                     {
10573                         cls : "", 
10574                         cn: [
10575                             combobox
10576                         ]
10577                     }
10578
10579                 ];
10580                 
10581                 labelCfg = cfg.cn[0];
10582                 contentCfg = cfg.cn[1];
10583             }
10584             
10585             if(this.labelWidth > 12){
10586                 labelCfg.style = "width: " + this.labelWidth + 'px';
10587             }
10588             
10589             if(this.labelWidth < 13 && this.labelmd == 0){
10590                 this.labelmd = this.labelWidth;
10591             }
10592             
10593             if(this.labellg > 0){
10594                 labelCfg.cls += ' col-lg-' + this.labellg;
10595                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10596             }
10597             
10598             if(this.labelmd > 0){
10599                 labelCfg.cls += ' col-md-' + this.labelmd;
10600                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10601             }
10602             
10603             if(this.labelsm > 0){
10604                 labelCfg.cls += ' col-sm-' + this.labelsm;
10605                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10606             }
10607             
10608             if(this.labelxs > 0){
10609                 labelCfg.cls += ' col-xs-' + this.labelxs;
10610                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10611             }
10612             
10613         } else if ( this.fieldLabel.length) {
10614 //                Roo.log(" label");
10615             cfg.cn = [
10616                 indicator,
10617                {
10618                    tag: 'label',
10619                    //cls : 'input-group-addon',
10620                    html : this.fieldLabel
10621
10622                },
10623
10624                combobox
10625
10626             ];
10627             
10628             if(this.indicatorpos == 'right'){
10629                 
10630                 cfg.cn = [
10631                     {
10632                        tag: 'label',
10633                        cn : [
10634                            {
10635                                tag : 'span',
10636                                html : this.fieldLabel
10637                            },
10638                            indicator
10639                        ]
10640
10641                     },
10642                     combobox
10643
10644                 ];
10645
10646             }
10647
10648         } else {
10649             
10650 //                Roo.log(" no label && no align");
10651                 cfg = combobox
10652                      
10653                 
10654         }
10655         
10656         var settings=this;
10657         ['xs','sm','md','lg'].map(function(size){
10658             if (settings[size]) {
10659                 cfg.cls += ' col-' + size + '-' + settings[size];
10660             }
10661         });
10662         
10663         return cfg;
10664         
10665     },
10666     
10667     
10668     
10669     // private
10670     onResize : function(w, h){
10671 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10672 //        if(typeof w == 'number'){
10673 //            var x = w - this.trigger.getWidth();
10674 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10675 //            this.trigger.setStyle('left', x+'px');
10676 //        }
10677     },
10678
10679     // private
10680     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10681
10682     // private
10683     getResizeEl : function(){
10684         return this.inputEl();
10685     },
10686
10687     // private
10688     getPositionEl : function(){
10689         return this.inputEl();
10690     },
10691
10692     // private
10693     alignErrorIcon : function(){
10694         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10695     },
10696
10697     // private
10698     initEvents : function(){
10699         
10700         this.createList();
10701         
10702         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10703         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10704         if(!this.multiple && this.showToggleBtn){
10705             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10706             if(this.hideTrigger){
10707                 this.trigger.setDisplayed(false);
10708             }
10709             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10710         }
10711         
10712         if(this.multiple){
10713             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10714         }
10715         
10716         if(this.removable && !this.editable && !this.tickable){
10717             var close = this.closeTriggerEl();
10718             
10719             if(close){
10720                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10721                 close.on('click', this.removeBtnClick, this, close);
10722             }
10723         }
10724         
10725         //this.trigger.addClassOnOver('x-form-trigger-over');
10726         //this.trigger.addClassOnClick('x-form-trigger-click');
10727         
10728         //if(!this.width){
10729         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10730         //}
10731     },
10732     
10733     closeTriggerEl : function()
10734     {
10735         var close = this.el.select('.roo-combo-removable-btn', true).first();
10736         return close ? close : false;
10737     },
10738     
10739     removeBtnClick : function(e, h, el)
10740     {
10741         e.preventDefault();
10742         
10743         if(this.fireEvent("remove", this) !== false){
10744             this.reset();
10745             this.fireEvent("afterremove", this)
10746         }
10747     },
10748     
10749     createList : function()
10750     {
10751         this.list = Roo.get(document.body).createChild({
10752             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10753             cls: 'typeahead typeahead-long dropdown-menu',
10754             style: 'display:none'
10755         });
10756         
10757         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10758         
10759     },
10760
10761     // private
10762     initTrigger : function(){
10763        
10764     },
10765
10766     // private
10767     onDestroy : function(){
10768         if(this.trigger){
10769             this.trigger.removeAllListeners();
10770           //  this.trigger.remove();
10771         }
10772         //if(this.wrap){
10773         //    this.wrap.remove();
10774         //}
10775         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10776     },
10777
10778     // private
10779     onFocus : function(){
10780         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10781         /*
10782         if(!this.mimicing){
10783             this.wrap.addClass('x-trigger-wrap-focus');
10784             this.mimicing = true;
10785             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10786             if(this.monitorTab){
10787                 this.el.on("keydown", this.checkTab, this);
10788             }
10789         }
10790         */
10791     },
10792
10793     // private
10794     checkTab : function(e){
10795         if(e.getKey() == e.TAB){
10796             this.triggerBlur();
10797         }
10798     },
10799
10800     // private
10801     onBlur : function(){
10802         // do nothing
10803     },
10804
10805     // private
10806     mimicBlur : function(e, t){
10807         /*
10808         if(!this.wrap.contains(t) && this.validateBlur()){
10809             this.triggerBlur();
10810         }
10811         */
10812     },
10813
10814     // private
10815     triggerBlur : function(){
10816         this.mimicing = false;
10817         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10818         if(this.monitorTab){
10819             this.el.un("keydown", this.checkTab, this);
10820         }
10821         //this.wrap.removeClass('x-trigger-wrap-focus');
10822         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10823     },
10824
10825     // private
10826     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10827     validateBlur : function(e, t){
10828         return true;
10829     },
10830
10831     // private
10832     onDisable : function(){
10833         this.inputEl().dom.disabled = true;
10834         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10835         //if(this.wrap){
10836         //    this.wrap.addClass('x-item-disabled');
10837         //}
10838     },
10839
10840     // private
10841     onEnable : function(){
10842         this.inputEl().dom.disabled = false;
10843         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10844         //if(this.wrap){
10845         //    this.el.removeClass('x-item-disabled');
10846         //}
10847     },
10848
10849     // private
10850     onShow : function(){
10851         var ae = this.getActionEl();
10852         
10853         if(ae){
10854             ae.dom.style.display = '';
10855             ae.dom.style.visibility = 'visible';
10856         }
10857     },
10858
10859     // private
10860     
10861     onHide : function(){
10862         var ae = this.getActionEl();
10863         ae.dom.style.display = 'none';
10864     },
10865
10866     /**
10867      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10868      * by an implementing function.
10869      * @method
10870      * @param {EventObject} e
10871      */
10872     onTriggerClick : Roo.emptyFn
10873 });
10874  /*
10875  * Based on:
10876  * Ext JS Library 1.1.1
10877  * Copyright(c) 2006-2007, Ext JS, LLC.
10878  *
10879  * Originally Released Under LGPL - original licence link has changed is not relivant.
10880  *
10881  * Fork - LGPL
10882  * <script type="text/javascript">
10883  */
10884
10885
10886 /**
10887  * @class Roo.data.SortTypes
10888  * @singleton
10889  * Defines the default sorting (casting?) comparison functions used when sorting data.
10890  */
10891 Roo.data.SortTypes = {
10892     /**
10893      * Default sort that does nothing
10894      * @param {Mixed} s The value being converted
10895      * @return {Mixed} The comparison value
10896      */
10897     none : function(s){
10898         return s;
10899     },
10900     
10901     /**
10902      * The regular expression used to strip tags
10903      * @type {RegExp}
10904      * @property
10905      */
10906     stripTagsRE : /<\/?[^>]+>/gi,
10907     
10908     /**
10909      * Strips all HTML tags to sort on text only
10910      * @param {Mixed} s The value being converted
10911      * @return {String} The comparison value
10912      */
10913     asText : function(s){
10914         return String(s).replace(this.stripTagsRE, "");
10915     },
10916     
10917     /**
10918      * Strips all HTML tags to sort on text only - Case insensitive
10919      * @param {Mixed} s The value being converted
10920      * @return {String} The comparison value
10921      */
10922     asUCText : function(s){
10923         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10924     },
10925     
10926     /**
10927      * Case insensitive string
10928      * @param {Mixed} s The value being converted
10929      * @return {String} The comparison value
10930      */
10931     asUCString : function(s) {
10932         return String(s).toUpperCase();
10933     },
10934     
10935     /**
10936      * Date sorting
10937      * @param {Mixed} s The value being converted
10938      * @return {Number} The comparison value
10939      */
10940     asDate : function(s) {
10941         if(!s){
10942             return 0;
10943         }
10944         if(s instanceof Date){
10945             return s.getTime();
10946         }
10947         return Date.parse(String(s));
10948     },
10949     
10950     /**
10951      * Float sorting
10952      * @param {Mixed} s The value being converted
10953      * @return {Float} The comparison value
10954      */
10955     asFloat : function(s) {
10956         var val = parseFloat(String(s).replace(/,/g, ""));
10957         if(isNaN(val)) {
10958             val = 0;
10959         }
10960         return val;
10961     },
10962     
10963     /**
10964      * Integer sorting
10965      * @param {Mixed} s The value being converted
10966      * @return {Number} The comparison value
10967      */
10968     asInt : function(s) {
10969         var val = parseInt(String(s).replace(/,/g, ""));
10970         if(isNaN(val)) {
10971             val = 0;
10972         }
10973         return val;
10974     }
10975 };/*
10976  * Based on:
10977  * Ext JS Library 1.1.1
10978  * Copyright(c) 2006-2007, Ext JS, LLC.
10979  *
10980  * Originally Released Under LGPL - original licence link has changed is not relivant.
10981  *
10982  * Fork - LGPL
10983  * <script type="text/javascript">
10984  */
10985
10986 /**
10987 * @class Roo.data.Record
10988  * Instances of this class encapsulate both record <em>definition</em> information, and record
10989  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10990  * to access Records cached in an {@link Roo.data.Store} object.<br>
10991  * <p>
10992  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10993  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10994  * objects.<br>
10995  * <p>
10996  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10997  * @constructor
10998  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10999  * {@link #create}. The parameters are the same.
11000  * @param {Array} data An associative Array of data values keyed by the field name.
11001  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11002  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11003  * not specified an integer id is generated.
11004  */
11005 Roo.data.Record = function(data, id){
11006     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11007     this.data = data;
11008 };
11009
11010 /**
11011  * Generate a constructor for a specific record layout.
11012  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11013  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11014  * Each field definition object may contain the following properties: <ul>
11015  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
11016  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11017  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11018  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11019  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11020  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11021  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11022  * this may be omitted.</p></li>
11023  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11024  * <ul><li>auto (Default, implies no conversion)</li>
11025  * <li>string</li>
11026  * <li>int</li>
11027  * <li>float</li>
11028  * <li>boolean</li>
11029  * <li>date</li></ul></p></li>
11030  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11031  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11032  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11033  * by the Reader into an object that will be stored in the Record. It is passed the
11034  * following parameters:<ul>
11035  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11036  * </ul></p></li>
11037  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11038  * </ul>
11039  * <br>usage:<br><pre><code>
11040 var TopicRecord = Roo.data.Record.create(
11041     {name: 'title', mapping: 'topic_title'},
11042     {name: 'author', mapping: 'username'},
11043     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11044     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11045     {name: 'lastPoster', mapping: 'user2'},
11046     {name: 'excerpt', mapping: 'post_text'}
11047 );
11048
11049 var myNewRecord = new TopicRecord({
11050     title: 'Do my job please',
11051     author: 'noobie',
11052     totalPosts: 1,
11053     lastPost: new Date(),
11054     lastPoster: 'Animal',
11055     excerpt: 'No way dude!'
11056 });
11057 myStore.add(myNewRecord);
11058 </code></pre>
11059  * @method create
11060  * @static
11061  */
11062 Roo.data.Record.create = function(o){
11063     var f = function(){
11064         f.superclass.constructor.apply(this, arguments);
11065     };
11066     Roo.extend(f, Roo.data.Record);
11067     var p = f.prototype;
11068     p.fields = new Roo.util.MixedCollection(false, function(field){
11069         return field.name;
11070     });
11071     for(var i = 0, len = o.length; i < len; i++){
11072         p.fields.add(new Roo.data.Field(o[i]));
11073     }
11074     f.getField = function(name){
11075         return p.fields.get(name);  
11076     };
11077     return f;
11078 };
11079
11080 Roo.data.Record.AUTO_ID = 1000;
11081 Roo.data.Record.EDIT = 'edit';
11082 Roo.data.Record.REJECT = 'reject';
11083 Roo.data.Record.COMMIT = 'commit';
11084
11085 Roo.data.Record.prototype = {
11086     /**
11087      * Readonly flag - true if this record has been modified.
11088      * @type Boolean
11089      */
11090     dirty : false,
11091     editing : false,
11092     error: null,
11093     modified: null,
11094
11095     // private
11096     join : function(store){
11097         this.store = store;
11098     },
11099
11100     /**
11101      * Set the named field to the specified value.
11102      * @param {String} name The name of the field to set.
11103      * @param {Object} value The value to set the field to.
11104      */
11105     set : function(name, value){
11106         if(this.data[name] == value){
11107             return;
11108         }
11109         this.dirty = true;
11110         if(!this.modified){
11111             this.modified = {};
11112         }
11113         if(typeof this.modified[name] == 'undefined'){
11114             this.modified[name] = this.data[name];
11115         }
11116         this.data[name] = value;
11117         if(!this.editing && this.store){
11118             this.store.afterEdit(this);
11119         }       
11120     },
11121
11122     /**
11123      * Get the value of the named field.
11124      * @param {String} name The name of the field to get the value of.
11125      * @return {Object} The value of the field.
11126      */
11127     get : function(name){
11128         return this.data[name]; 
11129     },
11130
11131     // private
11132     beginEdit : function(){
11133         this.editing = true;
11134         this.modified = {}; 
11135     },
11136
11137     // private
11138     cancelEdit : function(){
11139         this.editing = false;
11140         delete this.modified;
11141     },
11142
11143     // private
11144     endEdit : function(){
11145         this.editing = false;
11146         if(this.dirty && this.store){
11147             this.store.afterEdit(this);
11148         }
11149     },
11150
11151     /**
11152      * Usually called by the {@link Roo.data.Store} which owns the Record.
11153      * Rejects all changes made to the Record since either creation, or the last commit operation.
11154      * Modified fields are reverted to their original values.
11155      * <p>
11156      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11157      * of reject operations.
11158      */
11159     reject : function(){
11160         var m = this.modified;
11161         for(var n in m){
11162             if(typeof m[n] != "function"){
11163                 this.data[n] = m[n];
11164             }
11165         }
11166         this.dirty = false;
11167         delete this.modified;
11168         this.editing = false;
11169         if(this.store){
11170             this.store.afterReject(this);
11171         }
11172     },
11173
11174     /**
11175      * Usually called by the {@link Roo.data.Store} which owns the Record.
11176      * Commits all changes made to the Record since either creation, or the last commit operation.
11177      * <p>
11178      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11179      * of commit operations.
11180      */
11181     commit : function(){
11182         this.dirty = false;
11183         delete this.modified;
11184         this.editing = false;
11185         if(this.store){
11186             this.store.afterCommit(this);
11187         }
11188     },
11189
11190     // private
11191     hasError : function(){
11192         return this.error != null;
11193     },
11194
11195     // private
11196     clearError : function(){
11197         this.error = null;
11198     },
11199
11200     /**
11201      * Creates a copy of this record.
11202      * @param {String} id (optional) A new record id if you don't want to use this record's id
11203      * @return {Record}
11204      */
11205     copy : function(newId) {
11206         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11207     }
11208 };/*
11209  * Based on:
11210  * Ext JS Library 1.1.1
11211  * Copyright(c) 2006-2007, Ext JS, LLC.
11212  *
11213  * Originally Released Under LGPL - original licence link has changed is not relivant.
11214  *
11215  * Fork - LGPL
11216  * <script type="text/javascript">
11217  */
11218
11219
11220
11221 /**
11222  * @class Roo.data.Store
11223  * @extends Roo.util.Observable
11224  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11225  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11226  * <p>
11227  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
11228  * has no knowledge of the format of the data returned by the Proxy.<br>
11229  * <p>
11230  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11231  * instances from the data object. These records are cached and made available through accessor functions.
11232  * @constructor
11233  * Creates a new Store.
11234  * @param {Object} config A config object containing the objects needed for the Store to access data,
11235  * and read the data into Records.
11236  */
11237 Roo.data.Store = function(config){
11238     this.data = new Roo.util.MixedCollection(false);
11239     this.data.getKey = function(o){
11240         return o.id;
11241     };
11242     this.baseParams = {};
11243     // private
11244     this.paramNames = {
11245         "start" : "start",
11246         "limit" : "limit",
11247         "sort" : "sort",
11248         "dir" : "dir",
11249         "multisort" : "_multisort"
11250     };
11251
11252     if(config && config.data){
11253         this.inlineData = config.data;
11254         delete config.data;
11255     }
11256
11257     Roo.apply(this, config);
11258     
11259     if(this.reader){ // reader passed
11260         this.reader = Roo.factory(this.reader, Roo.data);
11261         this.reader.xmodule = this.xmodule || false;
11262         if(!this.recordType){
11263             this.recordType = this.reader.recordType;
11264         }
11265         if(this.reader.onMetaChange){
11266             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11267         }
11268     }
11269
11270     if(this.recordType){
11271         this.fields = this.recordType.prototype.fields;
11272     }
11273     this.modified = [];
11274
11275     this.addEvents({
11276         /**
11277          * @event datachanged
11278          * Fires when the data cache has changed, and a widget which is using this Store
11279          * as a Record cache should refresh its view.
11280          * @param {Store} this
11281          */
11282         datachanged : true,
11283         /**
11284          * @event metachange
11285          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11286          * @param {Store} this
11287          * @param {Object} meta The JSON metadata
11288          */
11289         metachange : true,
11290         /**
11291          * @event add
11292          * Fires when Records have been added to the Store
11293          * @param {Store} this
11294          * @param {Roo.data.Record[]} records The array of Records added
11295          * @param {Number} index The index at which the record(s) were added
11296          */
11297         add : true,
11298         /**
11299          * @event remove
11300          * Fires when a Record has been removed from the Store
11301          * @param {Store} this
11302          * @param {Roo.data.Record} record The Record that was removed
11303          * @param {Number} index The index at which the record was removed
11304          */
11305         remove : true,
11306         /**
11307          * @event update
11308          * Fires when a Record has been updated
11309          * @param {Store} this
11310          * @param {Roo.data.Record} record The Record that was updated
11311          * @param {String} operation The update operation being performed.  Value may be one of:
11312          * <pre><code>
11313  Roo.data.Record.EDIT
11314  Roo.data.Record.REJECT
11315  Roo.data.Record.COMMIT
11316          * </code></pre>
11317          */
11318         update : true,
11319         /**
11320          * @event clear
11321          * Fires when the data cache has been cleared.
11322          * @param {Store} this
11323          */
11324         clear : true,
11325         /**
11326          * @event beforeload
11327          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11328          * the load action will be canceled.
11329          * @param {Store} this
11330          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11331          */
11332         beforeload : true,
11333         /**
11334          * @event beforeloadadd
11335          * Fires after a new set of Records has been loaded.
11336          * @param {Store} this
11337          * @param {Roo.data.Record[]} records The Records that were loaded
11338          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11339          */
11340         beforeloadadd : true,
11341         /**
11342          * @event load
11343          * Fires after a new set of Records has been loaded, before they are added to the store.
11344          * @param {Store} this
11345          * @param {Roo.data.Record[]} records The Records that were loaded
11346          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11347          * @params {Object} return from reader
11348          */
11349         load : true,
11350         /**
11351          * @event loadexception
11352          * Fires if an exception occurs in the Proxy during loading.
11353          * Called with the signature of the Proxy's "loadexception" event.
11354          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11355          * 
11356          * @param {Proxy} 
11357          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11358          * @param {Object} load options 
11359          * @param {Object} jsonData from your request (normally this contains the Exception)
11360          */
11361         loadexception : true
11362     });
11363     
11364     if(this.proxy){
11365         this.proxy = Roo.factory(this.proxy, Roo.data);
11366         this.proxy.xmodule = this.xmodule || false;
11367         this.relayEvents(this.proxy,  ["loadexception"]);
11368     }
11369     this.sortToggle = {};
11370     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11371
11372     Roo.data.Store.superclass.constructor.call(this);
11373
11374     if(this.inlineData){
11375         this.loadData(this.inlineData);
11376         delete this.inlineData;
11377     }
11378 };
11379
11380 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11381      /**
11382     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11383     * without a remote query - used by combo/forms at present.
11384     */
11385     
11386     /**
11387     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11388     */
11389     /**
11390     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11391     */
11392     /**
11393     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11394     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11395     */
11396     /**
11397     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11398     * on any HTTP request
11399     */
11400     /**
11401     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11402     */
11403     /**
11404     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11405     */
11406     multiSort: false,
11407     /**
11408     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11409     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11410     */
11411     remoteSort : false,
11412
11413     /**
11414     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11415      * loaded or when a record is removed. (defaults to false).
11416     */
11417     pruneModifiedRecords : false,
11418
11419     // private
11420     lastOptions : null,
11421
11422     /**
11423      * Add Records to the Store and fires the add event.
11424      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11425      */
11426     add : function(records){
11427         records = [].concat(records);
11428         for(var i = 0, len = records.length; i < len; i++){
11429             records[i].join(this);
11430         }
11431         var index = this.data.length;
11432         this.data.addAll(records);
11433         this.fireEvent("add", this, records, index);
11434     },
11435
11436     /**
11437      * Remove a Record from the Store and fires the remove event.
11438      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11439      */
11440     remove : function(record){
11441         var index = this.data.indexOf(record);
11442         this.data.removeAt(index);
11443  
11444         if(this.pruneModifiedRecords){
11445             this.modified.remove(record);
11446         }
11447         this.fireEvent("remove", this, record, index);
11448     },
11449
11450     /**
11451      * Remove all Records from the Store and fires the clear event.
11452      */
11453     removeAll : function(){
11454         this.data.clear();
11455         if(this.pruneModifiedRecords){
11456             this.modified = [];
11457         }
11458         this.fireEvent("clear", this);
11459     },
11460
11461     /**
11462      * Inserts Records to the Store at the given index and fires the add event.
11463      * @param {Number} index The start index at which to insert the passed Records.
11464      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11465      */
11466     insert : function(index, records){
11467         records = [].concat(records);
11468         for(var i = 0, len = records.length; i < len; i++){
11469             this.data.insert(index, records[i]);
11470             records[i].join(this);
11471         }
11472         this.fireEvent("add", this, records, index);
11473     },
11474
11475     /**
11476      * Get the index within the cache of the passed Record.
11477      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11478      * @return {Number} The index of the passed Record. Returns -1 if not found.
11479      */
11480     indexOf : function(record){
11481         return this.data.indexOf(record);
11482     },
11483
11484     /**
11485      * Get the index within the cache of the Record with the passed id.
11486      * @param {String} id The id of the Record to find.
11487      * @return {Number} The index of the Record. Returns -1 if not found.
11488      */
11489     indexOfId : function(id){
11490         return this.data.indexOfKey(id);
11491     },
11492
11493     /**
11494      * Get the Record with the specified id.
11495      * @param {String} id The id of the Record to find.
11496      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11497      */
11498     getById : function(id){
11499         return this.data.key(id);
11500     },
11501
11502     /**
11503      * Get the Record at the specified index.
11504      * @param {Number} index The index of the Record to find.
11505      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11506      */
11507     getAt : function(index){
11508         return this.data.itemAt(index);
11509     },
11510
11511     /**
11512      * Returns a range of Records between specified indices.
11513      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11514      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11515      * @return {Roo.data.Record[]} An array of Records
11516      */
11517     getRange : function(start, end){
11518         return this.data.getRange(start, end);
11519     },
11520
11521     // private
11522     storeOptions : function(o){
11523         o = Roo.apply({}, o);
11524         delete o.callback;
11525         delete o.scope;
11526         this.lastOptions = o;
11527     },
11528
11529     /**
11530      * Loads the Record cache from the configured Proxy using the configured Reader.
11531      * <p>
11532      * If using remote paging, then the first load call must specify the <em>start</em>
11533      * and <em>limit</em> properties in the options.params property to establish the initial
11534      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11535      * <p>
11536      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11537      * and this call will return before the new data has been loaded. Perform any post-processing
11538      * in a callback function, or in a "load" event handler.</strong>
11539      * <p>
11540      * @param {Object} options An object containing properties which control loading options:<ul>
11541      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11542      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11543      * passed the following arguments:<ul>
11544      * <li>r : Roo.data.Record[]</li>
11545      * <li>options: Options object from the load call</li>
11546      * <li>success: Boolean success indicator</li></ul></li>
11547      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11548      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11549      * </ul>
11550      */
11551     load : function(options){
11552         options = options || {};
11553         if(this.fireEvent("beforeload", this, options) !== false){
11554             this.storeOptions(options);
11555             var p = Roo.apply(options.params || {}, this.baseParams);
11556             // if meta was not loaded from remote source.. try requesting it.
11557             if (!this.reader.metaFromRemote) {
11558                 p._requestMeta = 1;
11559             }
11560             if(this.sortInfo && this.remoteSort){
11561                 var pn = this.paramNames;
11562                 p[pn["sort"]] = this.sortInfo.field;
11563                 p[pn["dir"]] = this.sortInfo.direction;
11564             }
11565             if (this.multiSort) {
11566                 var pn = this.paramNames;
11567                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11568             }
11569             
11570             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11571         }
11572     },
11573
11574     /**
11575      * Reloads the Record cache from the configured Proxy using the configured Reader and
11576      * the options from the last load operation performed.
11577      * @param {Object} options (optional) An object containing properties which may override the options
11578      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11579      * the most recently used options are reused).
11580      */
11581     reload : function(options){
11582         this.load(Roo.applyIf(options||{}, this.lastOptions));
11583     },
11584
11585     // private
11586     // Called as a callback by the Reader during a load operation.
11587     loadRecords : function(o, options, success){
11588         if(!o || success === false){
11589             if(success !== false){
11590                 this.fireEvent("load", this, [], options, o);
11591             }
11592             if(options.callback){
11593                 options.callback.call(options.scope || this, [], options, false);
11594             }
11595             return;
11596         }
11597         // if data returned failure - throw an exception.
11598         if (o.success === false) {
11599             // show a message if no listener is registered.
11600             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11601                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11602             }
11603             // loadmask wil be hooked into this..
11604             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11605             return;
11606         }
11607         var r = o.records, t = o.totalRecords || r.length;
11608         
11609         this.fireEvent("beforeloadadd", this, r, options, o);
11610         
11611         if(!options || options.add !== true){
11612             if(this.pruneModifiedRecords){
11613                 this.modified = [];
11614             }
11615             for(var i = 0, len = r.length; i < len; i++){
11616                 r[i].join(this);
11617             }
11618             if(this.snapshot){
11619                 this.data = this.snapshot;
11620                 delete this.snapshot;
11621             }
11622             this.data.clear();
11623             this.data.addAll(r);
11624             this.totalLength = t;
11625             this.applySort();
11626             this.fireEvent("datachanged", this);
11627         }else{
11628             this.totalLength = Math.max(t, this.data.length+r.length);
11629             this.add(r);
11630         }
11631         
11632         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11633                 
11634             var e = new Roo.data.Record({});
11635
11636             e.set(this.parent.displayField, this.parent.emptyTitle);
11637             e.set(this.parent.valueField, '');
11638
11639             this.insert(0, e);
11640         }
11641             
11642         this.fireEvent("load", this, r, options, o);
11643         if(options.callback){
11644             options.callback.call(options.scope || this, r, options, true);
11645         }
11646     },
11647
11648
11649     /**
11650      * Loads data from a passed data block. A Reader which understands the format of the data
11651      * must have been configured in the constructor.
11652      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11653      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11654      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11655      */
11656     loadData : function(o, append){
11657         var r = this.reader.readRecords(o);
11658         this.loadRecords(r, {add: append}, true);
11659     },
11660
11661     /**
11662      * Gets the number of cached records.
11663      * <p>
11664      * <em>If using paging, this may not be the total size of the dataset. If the data object
11665      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11666      * the data set size</em>
11667      */
11668     getCount : function(){
11669         return this.data.length || 0;
11670     },
11671
11672     /**
11673      * Gets the total number of records in the dataset as returned by the server.
11674      * <p>
11675      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11676      * the dataset size</em>
11677      */
11678     getTotalCount : function(){
11679         return this.totalLength || 0;
11680     },
11681
11682     /**
11683      * Returns the sort state of the Store as an object with two properties:
11684      * <pre><code>
11685  field {String} The name of the field by which the Records are sorted
11686  direction {String} The sort order, "ASC" or "DESC"
11687      * </code></pre>
11688      */
11689     getSortState : function(){
11690         return this.sortInfo;
11691     },
11692
11693     // private
11694     applySort : function(){
11695         if(this.sortInfo && !this.remoteSort){
11696             var s = this.sortInfo, f = s.field;
11697             var st = this.fields.get(f).sortType;
11698             var fn = function(r1, r2){
11699                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11700                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11701             };
11702             this.data.sort(s.direction, fn);
11703             if(this.snapshot && this.snapshot != this.data){
11704                 this.snapshot.sort(s.direction, fn);
11705             }
11706         }
11707     },
11708
11709     /**
11710      * Sets the default sort column and order to be used by the next load operation.
11711      * @param {String} fieldName The name of the field to sort by.
11712      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11713      */
11714     setDefaultSort : function(field, dir){
11715         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11716     },
11717
11718     /**
11719      * Sort the Records.
11720      * If remote sorting is used, the sort is performed on the server, and the cache is
11721      * reloaded. If local sorting is used, the cache is sorted internally.
11722      * @param {String} fieldName The name of the field to sort by.
11723      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11724      */
11725     sort : function(fieldName, dir){
11726         var f = this.fields.get(fieldName);
11727         if(!dir){
11728             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11729             
11730             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11731                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11732             }else{
11733                 dir = f.sortDir;
11734             }
11735         }
11736         this.sortToggle[f.name] = dir;
11737         this.sortInfo = {field: f.name, direction: dir};
11738         if(!this.remoteSort){
11739             this.applySort();
11740             this.fireEvent("datachanged", this);
11741         }else{
11742             this.load(this.lastOptions);
11743         }
11744     },
11745
11746     /**
11747      * Calls the specified function for each of the Records in the cache.
11748      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11749      * Returning <em>false</em> aborts and exits the iteration.
11750      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11751      */
11752     each : function(fn, scope){
11753         this.data.each(fn, scope);
11754     },
11755
11756     /**
11757      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11758      * (e.g., during paging).
11759      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11760      */
11761     getModifiedRecords : function(){
11762         return this.modified;
11763     },
11764
11765     // private
11766     createFilterFn : function(property, value, anyMatch){
11767         if(!value.exec){ // not a regex
11768             value = String(value);
11769             if(value.length == 0){
11770                 return false;
11771             }
11772             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11773         }
11774         return function(r){
11775             return value.test(r.data[property]);
11776         };
11777     },
11778
11779     /**
11780      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11781      * @param {String} property A field on your records
11782      * @param {Number} start The record index to start at (defaults to 0)
11783      * @param {Number} end The last record index to include (defaults to length - 1)
11784      * @return {Number} The sum
11785      */
11786     sum : function(property, start, end){
11787         var rs = this.data.items, v = 0;
11788         start = start || 0;
11789         end = (end || end === 0) ? end : rs.length-1;
11790
11791         for(var i = start; i <= end; i++){
11792             v += (rs[i].data[property] || 0);
11793         }
11794         return v;
11795     },
11796
11797     /**
11798      * Filter the records by a specified property.
11799      * @param {String} field A field on your records
11800      * @param {String/RegExp} value Either a string that the field
11801      * should start with or a RegExp to test against the field
11802      * @param {Boolean} anyMatch True to match any part not just the beginning
11803      */
11804     filter : function(property, value, anyMatch){
11805         var fn = this.createFilterFn(property, value, anyMatch);
11806         return fn ? this.filterBy(fn) : this.clearFilter();
11807     },
11808
11809     /**
11810      * Filter by a function. The specified function will be called with each
11811      * record in this data source. If the function returns true the record is included,
11812      * otherwise it is filtered.
11813      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11814      * @param {Object} scope (optional) The scope of the function (defaults to this)
11815      */
11816     filterBy : function(fn, scope){
11817         this.snapshot = this.snapshot || this.data;
11818         this.data = this.queryBy(fn, scope||this);
11819         this.fireEvent("datachanged", this);
11820     },
11821
11822     /**
11823      * Query the records by a specified property.
11824      * @param {String} field A field on your records
11825      * @param {String/RegExp} value Either a string that the field
11826      * should start with or a RegExp to test against the field
11827      * @param {Boolean} anyMatch True to match any part not just the beginning
11828      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11829      */
11830     query : function(property, value, anyMatch){
11831         var fn = this.createFilterFn(property, value, anyMatch);
11832         return fn ? this.queryBy(fn) : this.data.clone();
11833     },
11834
11835     /**
11836      * Query by a function. The specified function will be called with each
11837      * record in this data source. If the function returns true the record is included
11838      * in the results.
11839      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11840      * @param {Object} scope (optional) The scope of the function (defaults to this)
11841       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11842      **/
11843     queryBy : function(fn, scope){
11844         var data = this.snapshot || this.data;
11845         return data.filterBy(fn, scope||this);
11846     },
11847
11848     /**
11849      * Collects unique values for a particular dataIndex from this store.
11850      * @param {String} dataIndex The property to collect
11851      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11852      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11853      * @return {Array} An array of the unique values
11854      **/
11855     collect : function(dataIndex, allowNull, bypassFilter){
11856         var d = (bypassFilter === true && this.snapshot) ?
11857                 this.snapshot.items : this.data.items;
11858         var v, sv, r = [], l = {};
11859         for(var i = 0, len = d.length; i < len; i++){
11860             v = d[i].data[dataIndex];
11861             sv = String(v);
11862             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11863                 l[sv] = true;
11864                 r[r.length] = v;
11865             }
11866         }
11867         return r;
11868     },
11869
11870     /**
11871      * Revert to a view of the Record cache with no filtering applied.
11872      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11873      */
11874     clearFilter : function(suppressEvent){
11875         if(this.snapshot && this.snapshot != this.data){
11876             this.data = this.snapshot;
11877             delete this.snapshot;
11878             if(suppressEvent !== true){
11879                 this.fireEvent("datachanged", this);
11880             }
11881         }
11882     },
11883
11884     // private
11885     afterEdit : function(record){
11886         if(this.modified.indexOf(record) == -1){
11887             this.modified.push(record);
11888         }
11889         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11890     },
11891     
11892     // private
11893     afterReject : function(record){
11894         this.modified.remove(record);
11895         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11896     },
11897
11898     // private
11899     afterCommit : function(record){
11900         this.modified.remove(record);
11901         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11902     },
11903
11904     /**
11905      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11906      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11907      */
11908     commitChanges : function(){
11909         var m = this.modified.slice(0);
11910         this.modified = [];
11911         for(var i = 0, len = m.length; i < len; i++){
11912             m[i].commit();
11913         }
11914     },
11915
11916     /**
11917      * Cancel outstanding changes on all changed records.
11918      */
11919     rejectChanges : function(){
11920         var m = this.modified.slice(0);
11921         this.modified = [];
11922         for(var i = 0, len = m.length; i < len; i++){
11923             m[i].reject();
11924         }
11925     },
11926
11927     onMetaChange : function(meta, rtype, o){
11928         this.recordType = rtype;
11929         this.fields = rtype.prototype.fields;
11930         delete this.snapshot;
11931         this.sortInfo = meta.sortInfo || this.sortInfo;
11932         this.modified = [];
11933         this.fireEvent('metachange', this, this.reader.meta);
11934     },
11935     
11936     moveIndex : function(data, type)
11937     {
11938         var index = this.indexOf(data);
11939         
11940         var newIndex = index + type;
11941         
11942         this.remove(data);
11943         
11944         this.insert(newIndex, data);
11945         
11946     }
11947 });/*
11948  * Based on:
11949  * Ext JS Library 1.1.1
11950  * Copyright(c) 2006-2007, Ext JS, LLC.
11951  *
11952  * Originally Released Under LGPL - original licence link has changed is not relivant.
11953  *
11954  * Fork - LGPL
11955  * <script type="text/javascript">
11956  */
11957
11958 /**
11959  * @class Roo.data.SimpleStore
11960  * @extends Roo.data.Store
11961  * Small helper class to make creating Stores from Array data easier.
11962  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11963  * @cfg {Array} fields An array of field definition objects, or field name strings.
11964  * @cfg {Array} data The multi-dimensional array of data
11965  * @constructor
11966  * @param {Object} config
11967  */
11968 Roo.data.SimpleStore = function(config){
11969     Roo.data.SimpleStore.superclass.constructor.call(this, {
11970         isLocal : true,
11971         reader: new Roo.data.ArrayReader({
11972                 id: config.id
11973             },
11974             Roo.data.Record.create(config.fields)
11975         ),
11976         proxy : new Roo.data.MemoryProxy(config.data)
11977     });
11978     this.load();
11979 };
11980 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990
11991 /**
11992 /**
11993  * @extends Roo.data.Store
11994  * @class Roo.data.JsonStore
11995  * Small helper class to make creating Stores for JSON data easier. <br/>
11996 <pre><code>
11997 var store = new Roo.data.JsonStore({
11998     url: 'get-images.php',
11999     root: 'images',
12000     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12001 });
12002 </code></pre>
12003  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12004  * JsonReader and HttpProxy (unless inline data is provided).</b>
12005  * @cfg {Array} fields An array of field definition objects, or field name strings.
12006  * @constructor
12007  * @param {Object} config
12008  */
12009 Roo.data.JsonStore = function(c){
12010     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12011         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12012         reader: new Roo.data.JsonReader(c, c.fields)
12013     }));
12014 };
12015 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12016  * Based on:
12017  * Ext JS Library 1.1.1
12018  * Copyright(c) 2006-2007, Ext JS, LLC.
12019  *
12020  * Originally Released Under LGPL - original licence link has changed is not relivant.
12021  *
12022  * Fork - LGPL
12023  * <script type="text/javascript">
12024  */
12025
12026  
12027 Roo.data.Field = function(config){
12028     if(typeof config == "string"){
12029         config = {name: config};
12030     }
12031     Roo.apply(this, config);
12032     
12033     if(!this.type){
12034         this.type = "auto";
12035     }
12036     
12037     var st = Roo.data.SortTypes;
12038     // named sortTypes are supported, here we look them up
12039     if(typeof this.sortType == "string"){
12040         this.sortType = st[this.sortType];
12041     }
12042     
12043     // set default sortType for strings and dates
12044     if(!this.sortType){
12045         switch(this.type){
12046             case "string":
12047                 this.sortType = st.asUCString;
12048                 break;
12049             case "date":
12050                 this.sortType = st.asDate;
12051                 break;
12052             default:
12053                 this.sortType = st.none;
12054         }
12055     }
12056
12057     // define once
12058     var stripRe = /[\$,%]/g;
12059
12060     // prebuilt conversion function for this field, instead of
12061     // switching every time we're reading a value
12062     if(!this.convert){
12063         var cv, dateFormat = this.dateFormat;
12064         switch(this.type){
12065             case "":
12066             case "auto":
12067             case undefined:
12068                 cv = function(v){ return v; };
12069                 break;
12070             case "string":
12071                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12072                 break;
12073             case "int":
12074                 cv = function(v){
12075                     return v !== undefined && v !== null && v !== '' ?
12076                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12077                     };
12078                 break;
12079             case "float":
12080                 cv = function(v){
12081                     return v !== undefined && v !== null && v !== '' ?
12082                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12083                     };
12084                 break;
12085             case "bool":
12086             case "boolean":
12087                 cv = function(v){ return v === true || v === "true" || v == 1; };
12088                 break;
12089             case "date":
12090                 cv = function(v){
12091                     if(!v){
12092                         return '';
12093                     }
12094                     if(v instanceof Date){
12095                         return v;
12096                     }
12097                     if(dateFormat){
12098                         if(dateFormat == "timestamp"){
12099                             return new Date(v*1000);
12100                         }
12101                         return Date.parseDate(v, dateFormat);
12102                     }
12103                     var parsed = Date.parse(v);
12104                     return parsed ? new Date(parsed) : null;
12105                 };
12106              break;
12107             
12108         }
12109         this.convert = cv;
12110     }
12111 };
12112
12113 Roo.data.Field.prototype = {
12114     dateFormat: null,
12115     defaultValue: "",
12116     mapping: null,
12117     sortType : null,
12118     sortDir : "ASC"
12119 };/*
12120  * Based on:
12121  * Ext JS Library 1.1.1
12122  * Copyright(c) 2006-2007, Ext JS, LLC.
12123  *
12124  * Originally Released Under LGPL - original licence link has changed is not relivant.
12125  *
12126  * Fork - LGPL
12127  * <script type="text/javascript">
12128  */
12129  
12130 // Base class for reading structured data from a data source.  This class is intended to be
12131 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12132
12133 /**
12134  * @class Roo.data.DataReader
12135  * Base class for reading structured data from a data source.  This class is intended to be
12136  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12137  */
12138
12139 Roo.data.DataReader = function(meta, recordType){
12140     
12141     this.meta = meta;
12142     
12143     this.recordType = recordType instanceof Array ? 
12144         Roo.data.Record.create(recordType) : recordType;
12145 };
12146
12147 Roo.data.DataReader.prototype = {
12148      /**
12149      * Create an empty record
12150      * @param {Object} data (optional) - overlay some values
12151      * @return {Roo.data.Record} record created.
12152      */
12153     newRow :  function(d) {
12154         var da =  {};
12155         this.recordType.prototype.fields.each(function(c) {
12156             switch( c.type) {
12157                 case 'int' : da[c.name] = 0; break;
12158                 case 'date' : da[c.name] = new Date(); break;
12159                 case 'float' : da[c.name] = 0.0; break;
12160                 case 'boolean' : da[c.name] = false; break;
12161                 default : da[c.name] = ""; break;
12162             }
12163             
12164         });
12165         return new this.recordType(Roo.apply(da, d));
12166     }
12167     
12168 };/*
12169  * Based on:
12170  * Ext JS Library 1.1.1
12171  * Copyright(c) 2006-2007, Ext JS, LLC.
12172  *
12173  * Originally Released Under LGPL - original licence link has changed is not relivant.
12174  *
12175  * Fork - LGPL
12176  * <script type="text/javascript">
12177  */
12178
12179 /**
12180  * @class Roo.data.DataProxy
12181  * @extends Roo.data.Observable
12182  * This class is an abstract base class for implementations which provide retrieval of
12183  * unformatted data objects.<br>
12184  * <p>
12185  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12186  * (of the appropriate type which knows how to parse the data object) to provide a block of
12187  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12188  * <p>
12189  * Custom implementations must implement the load method as described in
12190  * {@link Roo.data.HttpProxy#load}.
12191  */
12192 Roo.data.DataProxy = function(){
12193     this.addEvents({
12194         /**
12195          * @event beforeload
12196          * Fires before a network request is made to retrieve a data object.
12197          * @param {Object} This DataProxy object.
12198          * @param {Object} params The params parameter to the load function.
12199          */
12200         beforeload : true,
12201         /**
12202          * @event load
12203          * Fires before the load method's callback is called.
12204          * @param {Object} This DataProxy object.
12205          * @param {Object} o The data object.
12206          * @param {Object} arg The callback argument object passed to the load function.
12207          */
12208         load : true,
12209         /**
12210          * @event loadexception
12211          * Fires if an Exception occurs during data retrieval.
12212          * @param {Object} This DataProxy object.
12213          * @param {Object} o The data object.
12214          * @param {Object} arg The callback argument object passed to the load function.
12215          * @param {Object} e The Exception.
12216          */
12217         loadexception : true
12218     });
12219     Roo.data.DataProxy.superclass.constructor.call(this);
12220 };
12221
12222 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12223
12224     /**
12225      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12226      */
12227 /*
12228  * Based on:
12229  * Ext JS Library 1.1.1
12230  * Copyright(c) 2006-2007, Ext JS, LLC.
12231  *
12232  * Originally Released Under LGPL - original licence link has changed is not relivant.
12233  *
12234  * Fork - LGPL
12235  * <script type="text/javascript">
12236  */
12237 /**
12238  * @class Roo.data.MemoryProxy
12239  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12240  * to the Reader when its load method is called.
12241  * @constructor
12242  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12243  */
12244 Roo.data.MemoryProxy = function(data){
12245     if (data.data) {
12246         data = data.data;
12247     }
12248     Roo.data.MemoryProxy.superclass.constructor.call(this);
12249     this.data = data;
12250 };
12251
12252 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12253     
12254     /**
12255      * Load data from the requested source (in this case an in-memory
12256      * data object passed to the constructor), read the data object into
12257      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12258      * process that block using the passed callback.
12259      * @param {Object} params This parameter is not used by the MemoryProxy class.
12260      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12261      * object into a block of Roo.data.Records.
12262      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12263      * The function must be passed <ul>
12264      * <li>The Record block object</li>
12265      * <li>The "arg" argument from the load function</li>
12266      * <li>A boolean success indicator</li>
12267      * </ul>
12268      * @param {Object} scope The scope in which to call the callback
12269      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12270      */
12271     load : function(params, reader, callback, scope, arg){
12272         params = params || {};
12273         var result;
12274         try {
12275             result = reader.readRecords(this.data);
12276         }catch(e){
12277             this.fireEvent("loadexception", this, arg, null, e);
12278             callback.call(scope, null, arg, false);
12279             return;
12280         }
12281         callback.call(scope, result, arg, true);
12282     },
12283     
12284     // private
12285     update : function(params, records){
12286         
12287     }
12288 });/*
12289  * Based on:
12290  * Ext JS Library 1.1.1
12291  * Copyright(c) 2006-2007, Ext JS, LLC.
12292  *
12293  * Originally Released Under LGPL - original licence link has changed is not relivant.
12294  *
12295  * Fork - LGPL
12296  * <script type="text/javascript">
12297  */
12298 /**
12299  * @class Roo.data.HttpProxy
12300  * @extends Roo.data.DataProxy
12301  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12302  * configured to reference a certain URL.<br><br>
12303  * <p>
12304  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12305  * from which the running page was served.<br><br>
12306  * <p>
12307  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12308  * <p>
12309  * Be aware that to enable the browser to parse an XML document, the server must set
12310  * the Content-Type header in the HTTP response to "text/xml".
12311  * @constructor
12312  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12313  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12314  * will be used to make the request.
12315  */
12316 Roo.data.HttpProxy = function(conn){
12317     Roo.data.HttpProxy.superclass.constructor.call(this);
12318     // is conn a conn config or a real conn?
12319     this.conn = conn;
12320     this.useAjax = !conn || !conn.events;
12321   
12322 };
12323
12324 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12325     // thse are take from connection...
12326     
12327     /**
12328      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12329      */
12330     /**
12331      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12332      * extra parameters to each request made by this object. (defaults to undefined)
12333      */
12334     /**
12335      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12336      *  to each request made by this object. (defaults to undefined)
12337      */
12338     /**
12339      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12340      */
12341     /**
12342      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12343      */
12344      /**
12345      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12346      * @type Boolean
12347      */
12348   
12349
12350     /**
12351      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12352      * @type Boolean
12353      */
12354     /**
12355      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12356      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12357      * a finer-grained basis than the DataProxy events.
12358      */
12359     getConnection : function(){
12360         return this.useAjax ? Roo.Ajax : this.conn;
12361     },
12362
12363     /**
12364      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12365      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12366      * process that block using the passed callback.
12367      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12368      * for the request to the remote server.
12369      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12370      * object into a block of Roo.data.Records.
12371      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12372      * The function must be passed <ul>
12373      * <li>The Record block object</li>
12374      * <li>The "arg" argument from the load function</li>
12375      * <li>A boolean success indicator</li>
12376      * </ul>
12377      * @param {Object} scope The scope in which to call the callback
12378      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12379      */
12380     load : function(params, reader, callback, scope, arg){
12381         if(this.fireEvent("beforeload", this, params) !== false){
12382             var  o = {
12383                 params : params || {},
12384                 request: {
12385                     callback : callback,
12386                     scope : scope,
12387                     arg : arg
12388                 },
12389                 reader: reader,
12390                 callback : this.loadResponse,
12391                 scope: this
12392             };
12393             if(this.useAjax){
12394                 Roo.applyIf(o, this.conn);
12395                 if(this.activeRequest){
12396                     Roo.Ajax.abort(this.activeRequest);
12397                 }
12398                 this.activeRequest = Roo.Ajax.request(o);
12399             }else{
12400                 this.conn.request(o);
12401             }
12402         }else{
12403             callback.call(scope||this, null, arg, false);
12404         }
12405     },
12406
12407     // private
12408     loadResponse : function(o, success, response){
12409         delete this.activeRequest;
12410         if(!success){
12411             this.fireEvent("loadexception", this, o, response);
12412             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12413             return;
12414         }
12415         var result;
12416         try {
12417             result = o.reader.read(response);
12418         }catch(e){
12419             this.fireEvent("loadexception", this, o, response, e);
12420             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12421             return;
12422         }
12423         
12424         this.fireEvent("load", this, o, o.request.arg);
12425         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12426     },
12427
12428     // private
12429     update : function(dataSet){
12430
12431     },
12432
12433     // private
12434     updateResponse : function(dataSet){
12435
12436     }
12437 });/*
12438  * Based on:
12439  * Ext JS Library 1.1.1
12440  * Copyright(c) 2006-2007, Ext JS, LLC.
12441  *
12442  * Originally Released Under LGPL - original licence link has changed is not relivant.
12443  *
12444  * Fork - LGPL
12445  * <script type="text/javascript">
12446  */
12447
12448 /**
12449  * @class Roo.data.ScriptTagProxy
12450  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12451  * other than the originating domain of the running page.<br><br>
12452  * <p>
12453  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12454  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12455  * <p>
12456  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12457  * source code that is used as the source inside a &lt;script> tag.<br><br>
12458  * <p>
12459  * In order for the browser to process the returned data, the server must wrap the data object
12460  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12461  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12462  * depending on whether the callback name was passed:
12463  * <p>
12464  * <pre><code>
12465 boolean scriptTag = false;
12466 String cb = request.getParameter("callback");
12467 if (cb != null) {
12468     scriptTag = true;
12469     response.setContentType("text/javascript");
12470 } else {
12471     response.setContentType("application/x-json");
12472 }
12473 Writer out = response.getWriter();
12474 if (scriptTag) {
12475     out.write(cb + "(");
12476 }
12477 out.print(dataBlock.toJsonString());
12478 if (scriptTag) {
12479     out.write(");");
12480 }
12481 </pre></code>
12482  *
12483  * @constructor
12484  * @param {Object} config A configuration object.
12485  */
12486 Roo.data.ScriptTagProxy = function(config){
12487     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12488     Roo.apply(this, config);
12489     this.head = document.getElementsByTagName("head")[0];
12490 };
12491
12492 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12493
12494 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12495     /**
12496      * @cfg {String} url The URL from which to request the data object.
12497      */
12498     /**
12499      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12500      */
12501     timeout : 30000,
12502     /**
12503      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12504      * the server the name of the callback function set up by the load call to process the returned data object.
12505      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12506      * javascript output which calls this named function passing the data object as its only parameter.
12507      */
12508     callbackParam : "callback",
12509     /**
12510      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12511      * name to the request.
12512      */
12513     nocache : true,
12514
12515     /**
12516      * Load data from the configured URL, read the data object into
12517      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12518      * process that block using the passed callback.
12519      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12520      * for the request to the remote server.
12521      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12522      * object into a block of Roo.data.Records.
12523      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12524      * The function must be passed <ul>
12525      * <li>The Record block object</li>
12526      * <li>The "arg" argument from the load function</li>
12527      * <li>A boolean success indicator</li>
12528      * </ul>
12529      * @param {Object} scope The scope in which to call the callback
12530      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12531      */
12532     load : function(params, reader, callback, scope, arg){
12533         if(this.fireEvent("beforeload", this, params) !== false){
12534
12535             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12536
12537             var url = this.url;
12538             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12539             if(this.nocache){
12540                 url += "&_dc=" + (new Date().getTime());
12541             }
12542             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12543             var trans = {
12544                 id : transId,
12545                 cb : "stcCallback"+transId,
12546                 scriptId : "stcScript"+transId,
12547                 params : params,
12548                 arg : arg,
12549                 url : url,
12550                 callback : callback,
12551                 scope : scope,
12552                 reader : reader
12553             };
12554             var conn = this;
12555
12556             window[trans.cb] = function(o){
12557                 conn.handleResponse(o, trans);
12558             };
12559
12560             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12561
12562             if(this.autoAbort !== false){
12563                 this.abort();
12564             }
12565
12566             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12567
12568             var script = document.createElement("script");
12569             script.setAttribute("src", url);
12570             script.setAttribute("type", "text/javascript");
12571             script.setAttribute("id", trans.scriptId);
12572             this.head.appendChild(script);
12573
12574             this.trans = trans;
12575         }else{
12576             callback.call(scope||this, null, arg, false);
12577         }
12578     },
12579
12580     // private
12581     isLoading : function(){
12582         return this.trans ? true : false;
12583     },
12584
12585     /**
12586      * Abort the current server request.
12587      */
12588     abort : function(){
12589         if(this.isLoading()){
12590             this.destroyTrans(this.trans);
12591         }
12592     },
12593
12594     // private
12595     destroyTrans : function(trans, isLoaded){
12596         this.head.removeChild(document.getElementById(trans.scriptId));
12597         clearTimeout(trans.timeoutId);
12598         if(isLoaded){
12599             window[trans.cb] = undefined;
12600             try{
12601                 delete window[trans.cb];
12602             }catch(e){}
12603         }else{
12604             // if hasn't been loaded, wait for load to remove it to prevent script error
12605             window[trans.cb] = function(){
12606                 window[trans.cb] = undefined;
12607                 try{
12608                     delete window[trans.cb];
12609                 }catch(e){}
12610             };
12611         }
12612     },
12613
12614     // private
12615     handleResponse : function(o, trans){
12616         this.trans = false;
12617         this.destroyTrans(trans, true);
12618         var result;
12619         try {
12620             result = trans.reader.readRecords(o);
12621         }catch(e){
12622             this.fireEvent("loadexception", this, o, trans.arg, e);
12623             trans.callback.call(trans.scope||window, null, trans.arg, false);
12624             return;
12625         }
12626         this.fireEvent("load", this, o, trans.arg);
12627         trans.callback.call(trans.scope||window, result, trans.arg, true);
12628     },
12629
12630     // private
12631     handleFailure : function(trans){
12632         this.trans = false;
12633         this.destroyTrans(trans, false);
12634         this.fireEvent("loadexception", this, null, trans.arg);
12635         trans.callback.call(trans.scope||window, null, trans.arg, false);
12636     }
12637 });/*
12638  * Based on:
12639  * Ext JS Library 1.1.1
12640  * Copyright(c) 2006-2007, Ext JS, LLC.
12641  *
12642  * Originally Released Under LGPL - original licence link has changed is not relivant.
12643  *
12644  * Fork - LGPL
12645  * <script type="text/javascript">
12646  */
12647
12648 /**
12649  * @class Roo.data.JsonReader
12650  * @extends Roo.data.DataReader
12651  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12652  * based on mappings in a provided Roo.data.Record constructor.
12653  * 
12654  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12655  * in the reply previously. 
12656  * 
12657  * <p>
12658  * Example code:
12659  * <pre><code>
12660 var RecordDef = Roo.data.Record.create([
12661     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12662     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12663 ]);
12664 var myReader = new Roo.data.JsonReader({
12665     totalProperty: "results",    // The property which contains the total dataset size (optional)
12666     root: "rows",                // The property which contains an Array of row objects
12667     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12668 }, RecordDef);
12669 </code></pre>
12670  * <p>
12671  * This would consume a JSON file like this:
12672  * <pre><code>
12673 { 'results': 2, 'rows': [
12674     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12675     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12676 }
12677 </code></pre>
12678  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12679  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12680  * paged from the remote server.
12681  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12682  * @cfg {String} root name of the property which contains the Array of row objects.
12683  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12684  * @cfg {Array} fields Array of field definition objects
12685  * @constructor
12686  * Create a new JsonReader
12687  * @param {Object} meta Metadata configuration options
12688  * @param {Object} recordType Either an Array of field definition objects,
12689  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12690  */
12691 Roo.data.JsonReader = function(meta, recordType){
12692     
12693     meta = meta || {};
12694     // set some defaults:
12695     Roo.applyIf(meta, {
12696         totalProperty: 'total',
12697         successProperty : 'success',
12698         root : 'data',
12699         id : 'id'
12700     });
12701     
12702     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12703 };
12704 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12705     
12706     /**
12707      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12708      * Used by Store query builder to append _requestMeta to params.
12709      * 
12710      */
12711     metaFromRemote : false,
12712     /**
12713      * This method is only used by a DataProxy which has retrieved data from a remote server.
12714      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12715      * @return {Object} data A data block which is used by an Roo.data.Store object as
12716      * a cache of Roo.data.Records.
12717      */
12718     read : function(response){
12719         var json = response.responseText;
12720        
12721         var o = /* eval:var:o */ eval("("+json+")");
12722         if(!o) {
12723             throw {message: "JsonReader.read: Json object not found"};
12724         }
12725         
12726         if(o.metaData){
12727             
12728             delete this.ef;
12729             this.metaFromRemote = true;
12730             this.meta = o.metaData;
12731             this.recordType = Roo.data.Record.create(o.metaData.fields);
12732             this.onMetaChange(this.meta, this.recordType, o);
12733         }
12734         return this.readRecords(o);
12735     },
12736
12737     // private function a store will implement
12738     onMetaChange : function(meta, recordType, o){
12739
12740     },
12741
12742     /**
12743          * @ignore
12744          */
12745     simpleAccess: function(obj, subsc) {
12746         return obj[subsc];
12747     },
12748
12749         /**
12750          * @ignore
12751          */
12752     getJsonAccessor: function(){
12753         var re = /[\[\.]/;
12754         return function(expr) {
12755             try {
12756                 return(re.test(expr))
12757                     ? new Function("obj", "return obj." + expr)
12758                     : function(obj){
12759                         return obj[expr];
12760                     };
12761             } catch(e){}
12762             return Roo.emptyFn;
12763         };
12764     }(),
12765
12766     /**
12767      * Create a data block containing Roo.data.Records from an XML document.
12768      * @param {Object} o An object which contains an Array of row objects in the property specified
12769      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12770      * which contains the total size of the dataset.
12771      * @return {Object} data A data block which is used by an Roo.data.Store object as
12772      * a cache of Roo.data.Records.
12773      */
12774     readRecords : function(o){
12775         /**
12776          * After any data loads, the raw JSON data is available for further custom processing.
12777          * @type Object
12778          */
12779         this.o = o;
12780         var s = this.meta, Record = this.recordType,
12781             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12782
12783 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12784         if (!this.ef) {
12785             if(s.totalProperty) {
12786                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12787                 }
12788                 if(s.successProperty) {
12789                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12790                 }
12791                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12792                 if (s.id) {
12793                         var g = this.getJsonAccessor(s.id);
12794                         this.getId = function(rec) {
12795                                 var r = g(rec);  
12796                                 return (r === undefined || r === "") ? null : r;
12797                         };
12798                 } else {
12799                         this.getId = function(){return null;};
12800                 }
12801             this.ef = [];
12802             for(var jj = 0; jj < fl; jj++){
12803                 f = fi[jj];
12804                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12805                 this.ef[jj] = this.getJsonAccessor(map);
12806             }
12807         }
12808
12809         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12810         if(s.totalProperty){
12811             var vt = parseInt(this.getTotal(o), 10);
12812             if(!isNaN(vt)){
12813                 totalRecords = vt;
12814             }
12815         }
12816         if(s.successProperty){
12817             var vs = this.getSuccess(o);
12818             if(vs === false || vs === 'false'){
12819                 success = false;
12820             }
12821         }
12822         var records = [];
12823         for(var i = 0; i < c; i++){
12824                 var n = root[i];
12825             var values = {};
12826             var id = this.getId(n);
12827             for(var j = 0; j < fl; j++){
12828                 f = fi[j];
12829             var v = this.ef[j](n);
12830             if (!f.convert) {
12831                 Roo.log('missing convert for ' + f.name);
12832                 Roo.log(f);
12833                 continue;
12834             }
12835             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12836             }
12837             var record = new Record(values, id);
12838             record.json = n;
12839             records[i] = record;
12840         }
12841         return {
12842             raw : o,
12843             success : success,
12844             records : records,
12845             totalRecords : totalRecords
12846         };
12847     }
12848 });/*
12849  * Based on:
12850  * Ext JS Library 1.1.1
12851  * Copyright(c) 2006-2007, Ext JS, LLC.
12852  *
12853  * Originally Released Under LGPL - original licence link has changed is not relivant.
12854  *
12855  * Fork - LGPL
12856  * <script type="text/javascript">
12857  */
12858
12859 /**
12860  * @class Roo.data.ArrayReader
12861  * @extends Roo.data.DataReader
12862  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12863  * Each element of that Array represents a row of data fields. The
12864  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12865  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12866  * <p>
12867  * Example code:.
12868  * <pre><code>
12869 var RecordDef = Roo.data.Record.create([
12870     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12871     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12872 ]);
12873 var myReader = new Roo.data.ArrayReader({
12874     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12875 }, RecordDef);
12876 </code></pre>
12877  * <p>
12878  * This would consume an Array like this:
12879  * <pre><code>
12880 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12881   </code></pre>
12882  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12883  * @constructor
12884  * Create a new JsonReader
12885  * @param {Object} meta Metadata configuration options.
12886  * @param {Object} recordType Either an Array of field definition objects
12887  * as specified to {@link Roo.data.Record#create},
12888  * or an {@link Roo.data.Record} object
12889  * created using {@link Roo.data.Record#create}.
12890  */
12891 Roo.data.ArrayReader = function(meta, recordType){
12892     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12893 };
12894
12895 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12896     /**
12897      * Create a data block containing Roo.data.Records from an XML document.
12898      * @param {Object} o An Array of row objects which represents the dataset.
12899      * @return {Object} data A data block which is used by an Roo.data.Store object as
12900      * a cache of Roo.data.Records.
12901      */
12902     readRecords : function(o){
12903         var sid = this.meta ? this.meta.id : null;
12904         var recordType = this.recordType, fields = recordType.prototype.fields;
12905         var records = [];
12906         var root = o;
12907             for(var i = 0; i < root.length; i++){
12908                     var n = root[i];
12909                 var values = {};
12910                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12911                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12912                 var f = fields.items[j];
12913                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12914                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12915                 v = f.convert(v);
12916                 values[f.name] = v;
12917             }
12918                 var record = new recordType(values, id);
12919                 record.json = n;
12920                 records[records.length] = record;
12921             }
12922             return {
12923                 records : records,
12924                 totalRecords : records.length
12925             };
12926     }
12927 });/*
12928  * - LGPL
12929  * * 
12930  */
12931
12932 /**
12933  * @class Roo.bootstrap.ComboBox
12934  * @extends Roo.bootstrap.TriggerField
12935  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12936  * @cfg {Boolean} append (true|false) default false
12937  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12938  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12939  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12940  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12941  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12942  * @cfg {Boolean} animate default true
12943  * @cfg {Boolean} emptyResultText only for touch device
12944  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12945  * @cfg {String} emptyTitle default ''
12946  * @constructor
12947  * Create a new ComboBox.
12948  * @param {Object} config Configuration options
12949  */
12950 Roo.bootstrap.ComboBox = function(config){
12951     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12952     this.addEvents({
12953         /**
12954          * @event expand
12955          * Fires when the dropdown list is expanded
12956         * @param {Roo.bootstrap.ComboBox} combo This combo box
12957         */
12958         'expand' : true,
12959         /**
12960          * @event collapse
12961          * Fires when the dropdown list is collapsed
12962         * @param {Roo.bootstrap.ComboBox} combo This combo box
12963         */
12964         'collapse' : true,
12965         /**
12966          * @event beforeselect
12967          * Fires before a list item is selected. Return false to cancel the selection.
12968         * @param {Roo.bootstrap.ComboBox} combo This combo box
12969         * @param {Roo.data.Record} record The data record returned from the underlying store
12970         * @param {Number} index The index of the selected item in the dropdown list
12971         */
12972         'beforeselect' : true,
12973         /**
12974          * @event select
12975          * Fires when a list item is selected
12976         * @param {Roo.bootstrap.ComboBox} combo This combo box
12977         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12978         * @param {Number} index The index of the selected item in the dropdown list
12979         */
12980         'select' : true,
12981         /**
12982          * @event beforequery
12983          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12984          * The event object passed has these properties:
12985         * @param {Roo.bootstrap.ComboBox} combo This combo box
12986         * @param {String} query The query
12987         * @param {Boolean} forceAll true to force "all" query
12988         * @param {Boolean} cancel true to cancel the query
12989         * @param {Object} e The query event object
12990         */
12991         'beforequery': true,
12992          /**
12993          * @event add
12994          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12995         * @param {Roo.bootstrap.ComboBox} combo This combo box
12996         */
12997         'add' : true,
12998         /**
12999          * @event edit
13000          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13001         * @param {Roo.bootstrap.ComboBox} combo This combo box
13002         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13003         */
13004         'edit' : true,
13005         /**
13006          * @event remove
13007          * Fires when the remove value from the combobox array
13008         * @param {Roo.bootstrap.ComboBox} combo This combo box
13009         */
13010         'remove' : true,
13011         /**
13012          * @event afterremove
13013          * Fires when the remove value from the combobox array
13014         * @param {Roo.bootstrap.ComboBox} combo This combo box
13015         */
13016         'afterremove' : true,
13017         /**
13018          * @event specialfilter
13019          * Fires when specialfilter
13020             * @param {Roo.bootstrap.ComboBox} combo This combo box
13021             */
13022         'specialfilter' : true,
13023         /**
13024          * @event tick
13025          * Fires when tick the element
13026             * @param {Roo.bootstrap.ComboBox} combo This combo box
13027             */
13028         'tick' : true,
13029         /**
13030          * @event touchviewdisplay
13031          * Fires when touch view require special display (default is using displayField)
13032             * @param {Roo.bootstrap.ComboBox} combo This combo box
13033             * @param {Object} cfg set html .
13034             */
13035         'touchviewdisplay' : true
13036         
13037     });
13038     
13039     this.item = [];
13040     this.tickItems = [];
13041     
13042     this.selectedIndex = -1;
13043     if(this.mode == 'local'){
13044         if(config.queryDelay === undefined){
13045             this.queryDelay = 10;
13046         }
13047         if(config.minChars === undefined){
13048             this.minChars = 0;
13049         }
13050     }
13051 };
13052
13053 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13054      
13055     /**
13056      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13057      * rendering into an Roo.Editor, defaults to false)
13058      */
13059     /**
13060      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13061      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13062      */
13063     /**
13064      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13065      */
13066     /**
13067      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13068      * the dropdown list (defaults to undefined, with no header element)
13069      */
13070
13071      /**
13072      * @cfg {String/Roo.Template} tpl The template to use to render the output
13073      */
13074      
13075      /**
13076      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13077      */
13078     listWidth: undefined,
13079     /**
13080      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13081      * mode = 'remote' or 'text' if mode = 'local')
13082      */
13083     displayField: undefined,
13084     
13085     /**
13086      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13087      * mode = 'remote' or 'value' if mode = 'local'). 
13088      * Note: use of a valueField requires the user make a selection
13089      * in order for a value to be mapped.
13090      */
13091     valueField: undefined,
13092     /**
13093      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13094      */
13095     modalTitle : '',
13096     
13097     /**
13098      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13099      * field's data value (defaults to the underlying DOM element's name)
13100      */
13101     hiddenName: undefined,
13102     /**
13103      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13104      */
13105     listClass: '',
13106     /**
13107      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13108      */
13109     selectedClass: 'active',
13110     
13111     /**
13112      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13113      */
13114     shadow:'sides',
13115     /**
13116      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13117      * anchor positions (defaults to 'tl-bl')
13118      */
13119     listAlign: 'tl-bl?',
13120     /**
13121      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13122      */
13123     maxHeight: 300,
13124     /**
13125      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13126      * query specified by the allQuery config option (defaults to 'query')
13127      */
13128     triggerAction: 'query',
13129     /**
13130      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13131      * (defaults to 4, does not apply if editable = false)
13132      */
13133     minChars : 4,
13134     /**
13135      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13136      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13137      */
13138     typeAhead: false,
13139     /**
13140      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13141      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13142      */
13143     queryDelay: 500,
13144     /**
13145      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13146      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13147      */
13148     pageSize: 0,
13149     /**
13150      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13151      * when editable = true (defaults to false)
13152      */
13153     selectOnFocus:false,
13154     /**
13155      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13156      */
13157     queryParam: 'query',
13158     /**
13159      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13160      * when mode = 'remote' (defaults to 'Loading...')
13161      */
13162     loadingText: 'Loading...',
13163     /**
13164      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13165      */
13166     resizable: false,
13167     /**
13168      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13169      */
13170     handleHeight : 8,
13171     /**
13172      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13173      * traditional select (defaults to true)
13174      */
13175     editable: true,
13176     /**
13177      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13178      */
13179     allQuery: '',
13180     /**
13181      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13182      */
13183     mode: 'remote',
13184     /**
13185      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13186      * listWidth has a higher value)
13187      */
13188     minListWidth : 70,
13189     /**
13190      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13191      * allow the user to set arbitrary text into the field (defaults to false)
13192      */
13193     forceSelection:false,
13194     /**
13195      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13196      * if typeAhead = true (defaults to 250)
13197      */
13198     typeAheadDelay : 250,
13199     /**
13200      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13201      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13202      */
13203     valueNotFoundText : undefined,
13204     /**
13205      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13206      */
13207     blockFocus : false,
13208     
13209     /**
13210      * @cfg {Boolean} disableClear Disable showing of clear button.
13211      */
13212     disableClear : false,
13213     /**
13214      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13215      */
13216     alwaysQuery : false,
13217     
13218     /**
13219      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13220      */
13221     multiple : false,
13222     
13223     /**
13224      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13225      */
13226     invalidClass : "has-warning",
13227     
13228     /**
13229      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13230      */
13231     validClass : "has-success",
13232     
13233     /**
13234      * @cfg {Boolean} specialFilter (true|false) special filter default false
13235      */
13236     specialFilter : false,
13237     
13238     /**
13239      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13240      */
13241     mobileTouchView : true,
13242     
13243     /**
13244      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13245      */
13246     useNativeIOS : false,
13247     
13248     /**
13249      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13250      */
13251     mobile_restrict_height : false,
13252     
13253     ios_options : false,
13254     
13255     //private
13256     addicon : false,
13257     editicon: false,
13258     
13259     page: 0,
13260     hasQuery: false,
13261     append: false,
13262     loadNext: false,
13263     autoFocus : true,
13264     tickable : false,
13265     btnPosition : 'right',
13266     triggerList : true,
13267     showToggleBtn : true,
13268     animate : true,
13269     emptyResultText: 'Empty',
13270     triggerText : 'Select',
13271     emptyTitle : '',
13272     
13273     // element that contains real text value.. (when hidden is used..)
13274     
13275     getAutoCreate : function()
13276     {   
13277         var cfg = false;
13278         //render
13279         /*
13280          * Render classic select for iso
13281          */
13282         
13283         if(Roo.isIOS && this.useNativeIOS){
13284             cfg = this.getAutoCreateNativeIOS();
13285             return cfg;
13286         }
13287         
13288         /*
13289          * Touch Devices
13290          */
13291         
13292         if(Roo.isTouch && this.mobileTouchView){
13293             cfg = this.getAutoCreateTouchView();
13294             return cfg;;
13295         }
13296         
13297         /*
13298          *  Normal ComboBox
13299          */
13300         if(!this.tickable){
13301             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13302             return cfg;
13303         }
13304         
13305         /*
13306          *  ComboBox with tickable selections
13307          */
13308              
13309         var align = this.labelAlign || this.parentLabelAlign();
13310         
13311         cfg = {
13312             cls : 'form-group roo-combobox-tickable' //input-group
13313         };
13314         
13315         var btn_text_select = '';
13316         var btn_text_done = '';
13317         var btn_text_cancel = '';
13318         
13319         if (this.btn_text_show) {
13320             btn_text_select = 'Select';
13321             btn_text_done = 'Done';
13322             btn_text_cancel = 'Cancel'; 
13323         }
13324         
13325         var buttons = {
13326             tag : 'div',
13327             cls : 'tickable-buttons',
13328             cn : [
13329                 {
13330                     tag : 'button',
13331                     type : 'button',
13332                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13333                     //html : this.triggerText
13334                     html: btn_text_select
13335                 },
13336                 {
13337                     tag : 'button',
13338                     type : 'button',
13339                     name : 'ok',
13340                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13341                     //html : 'Done'
13342                     html: btn_text_done
13343                 },
13344                 {
13345                     tag : 'button',
13346                     type : 'button',
13347                     name : 'cancel',
13348                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13349                     //html : 'Cancel'
13350                     html: btn_text_cancel
13351                 }
13352             ]
13353         };
13354         
13355         if(this.editable){
13356             buttons.cn.unshift({
13357                 tag: 'input',
13358                 cls: 'roo-select2-search-field-input'
13359             });
13360         }
13361         
13362         var _this = this;
13363         
13364         Roo.each(buttons.cn, function(c){
13365             if (_this.size) {
13366                 c.cls += ' btn-' + _this.size;
13367             }
13368
13369             if (_this.disabled) {
13370                 c.disabled = true;
13371             }
13372         });
13373         
13374         var box = {
13375             tag: 'div',
13376             cn: [
13377                 {
13378                     tag: 'input',
13379                     type : 'hidden',
13380                     cls: 'form-hidden-field'
13381                 },
13382                 {
13383                     tag: 'ul',
13384                     cls: 'roo-select2-choices',
13385                     cn:[
13386                         {
13387                             tag: 'li',
13388                             cls: 'roo-select2-search-field',
13389                             cn: [
13390                                 buttons
13391                             ]
13392                         }
13393                     ]
13394                 }
13395             ]
13396         };
13397         
13398         var combobox = {
13399             cls: 'roo-select2-container input-group roo-select2-container-multi',
13400             cn: [
13401                 
13402                 box
13403 //                {
13404 //                    tag: 'ul',
13405 //                    cls: 'typeahead typeahead-long dropdown-menu',
13406 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13407 //                }
13408             ]
13409         };
13410         
13411         if(this.hasFeedback && !this.allowBlank){
13412             
13413             var feedback = {
13414                 tag: 'span',
13415                 cls: 'glyphicon form-control-feedback'
13416             };
13417
13418             combobox.cn.push(feedback);
13419         }
13420         
13421         var indicator = {
13422             tag : 'i',
13423             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13424             tooltip : 'This field is required'
13425         };
13426         if (Roo.bootstrap.version == 4) {
13427             indicator = {
13428                 tag : 'i',
13429                 style : 'display:none'
13430             };
13431         }
13432         if (align ==='left' && this.fieldLabel.length) {
13433             
13434             cfg.cls += ' roo-form-group-label-left row';
13435             
13436             cfg.cn = [
13437                 indicator,
13438                 {
13439                     tag: 'label',
13440                     'for' :  id,
13441                     cls : 'control-label col-form-label',
13442                     html : this.fieldLabel
13443
13444                 },
13445                 {
13446                     cls : "", 
13447                     cn: [
13448                         combobox
13449                     ]
13450                 }
13451
13452             ];
13453             
13454             var labelCfg = cfg.cn[1];
13455             var contentCfg = cfg.cn[2];
13456             
13457
13458             if(this.indicatorpos == 'right'){
13459                 
13460                 cfg.cn = [
13461                     {
13462                         tag: 'label',
13463                         'for' :  id,
13464                         cls : 'control-label col-form-label',
13465                         cn : [
13466                             {
13467                                 tag : 'span',
13468                                 html : this.fieldLabel
13469                             },
13470                             indicator
13471                         ]
13472                     },
13473                     {
13474                         cls : "",
13475                         cn: [
13476                             combobox
13477                         ]
13478                     }
13479
13480                 ];
13481                 
13482                 
13483                 
13484                 labelCfg = cfg.cn[0];
13485                 contentCfg = cfg.cn[1];
13486             
13487             }
13488             
13489             if(this.labelWidth > 12){
13490                 labelCfg.style = "width: " + this.labelWidth + 'px';
13491             }
13492             
13493             if(this.labelWidth < 13 && this.labelmd == 0){
13494                 this.labelmd = this.labelWidth;
13495             }
13496             
13497             if(this.labellg > 0){
13498                 labelCfg.cls += ' col-lg-' + this.labellg;
13499                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13500             }
13501             
13502             if(this.labelmd > 0){
13503                 labelCfg.cls += ' col-md-' + this.labelmd;
13504                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13505             }
13506             
13507             if(this.labelsm > 0){
13508                 labelCfg.cls += ' col-sm-' + this.labelsm;
13509                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13510             }
13511             
13512             if(this.labelxs > 0){
13513                 labelCfg.cls += ' col-xs-' + this.labelxs;
13514                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13515             }
13516                 
13517                 
13518         } else if ( this.fieldLabel.length) {
13519 //                Roo.log(" label");
13520                  cfg.cn = [
13521                    indicator,
13522                     {
13523                         tag: 'label',
13524                         //cls : 'input-group-addon',
13525                         html : this.fieldLabel
13526                     },
13527                     combobox
13528                 ];
13529                 
13530                 if(this.indicatorpos == 'right'){
13531                     cfg.cn = [
13532                         {
13533                             tag: 'label',
13534                             //cls : 'input-group-addon',
13535                             html : this.fieldLabel
13536                         },
13537                         indicator,
13538                         combobox
13539                     ];
13540                     
13541                 }
13542
13543         } else {
13544             
13545 //                Roo.log(" no label && no align");
13546                 cfg = combobox
13547                      
13548                 
13549         }
13550          
13551         var settings=this;
13552         ['xs','sm','md','lg'].map(function(size){
13553             if (settings[size]) {
13554                 cfg.cls += ' col-' + size + '-' + settings[size];
13555             }
13556         });
13557         
13558         return cfg;
13559         
13560     },
13561     
13562     _initEventsCalled : false,
13563     
13564     // private
13565     initEvents: function()
13566     {   
13567         if (this._initEventsCalled) { // as we call render... prevent looping...
13568             return;
13569         }
13570         this._initEventsCalled = true;
13571         
13572         if (!this.store) {
13573             throw "can not find store for combo";
13574         }
13575         
13576         this.indicator = this.indicatorEl();
13577         
13578         this.store = Roo.factory(this.store, Roo.data);
13579         this.store.parent = this;
13580         
13581         // if we are building from html. then this element is so complex, that we can not really
13582         // use the rendered HTML.
13583         // so we have to trash and replace the previous code.
13584         if (Roo.XComponent.build_from_html) {
13585             // remove this element....
13586             var e = this.el.dom, k=0;
13587             while (e ) { e = e.previousSibling;  ++k;}
13588
13589             this.el.remove();
13590             
13591             this.el=false;
13592             this.rendered = false;
13593             
13594             this.render(this.parent().getChildContainer(true), k);
13595         }
13596         
13597         if(Roo.isIOS && this.useNativeIOS){
13598             this.initIOSView();
13599             return;
13600         }
13601         
13602         /*
13603          * Touch Devices
13604          */
13605         
13606         if(Roo.isTouch && this.mobileTouchView){
13607             this.initTouchView();
13608             return;
13609         }
13610         
13611         if(this.tickable){
13612             this.initTickableEvents();
13613             return;
13614         }
13615         
13616         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13617         
13618         if(this.hiddenName){
13619             
13620             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13621             
13622             this.hiddenField.dom.value =
13623                 this.hiddenValue !== undefined ? this.hiddenValue :
13624                 this.value !== undefined ? this.value : '';
13625
13626             // prevent input submission
13627             this.el.dom.removeAttribute('name');
13628             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13629              
13630              
13631         }
13632         //if(Roo.isGecko){
13633         //    this.el.dom.setAttribute('autocomplete', 'off');
13634         //}
13635         
13636         var cls = 'x-combo-list';
13637         
13638         //this.list = new Roo.Layer({
13639         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13640         //});
13641         
13642         var _this = this;
13643         
13644         (function(){
13645             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13646             _this.list.setWidth(lw);
13647         }).defer(100);
13648         
13649         this.list.on('mouseover', this.onViewOver, this);
13650         this.list.on('mousemove', this.onViewMove, this);
13651         this.list.on('scroll', this.onViewScroll, this);
13652         
13653         /*
13654         this.list.swallowEvent('mousewheel');
13655         this.assetHeight = 0;
13656
13657         if(this.title){
13658             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13659             this.assetHeight += this.header.getHeight();
13660         }
13661
13662         this.innerList = this.list.createChild({cls:cls+'-inner'});
13663         this.innerList.on('mouseover', this.onViewOver, this);
13664         this.innerList.on('mousemove', this.onViewMove, this);
13665         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13666         
13667         if(this.allowBlank && !this.pageSize && !this.disableClear){
13668             this.footer = this.list.createChild({cls:cls+'-ft'});
13669             this.pageTb = new Roo.Toolbar(this.footer);
13670            
13671         }
13672         if(this.pageSize){
13673             this.footer = this.list.createChild({cls:cls+'-ft'});
13674             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13675                     {pageSize: this.pageSize});
13676             
13677         }
13678         
13679         if (this.pageTb && this.allowBlank && !this.disableClear) {
13680             var _this = this;
13681             this.pageTb.add(new Roo.Toolbar.Fill(), {
13682                 cls: 'x-btn-icon x-btn-clear',
13683                 text: '&#160;',
13684                 handler: function()
13685                 {
13686                     _this.collapse();
13687                     _this.clearValue();
13688                     _this.onSelect(false, -1);
13689                 }
13690             });
13691         }
13692         if (this.footer) {
13693             this.assetHeight += this.footer.getHeight();
13694         }
13695         */
13696             
13697         if(!this.tpl){
13698             this.tpl = Roo.bootstrap.version == 4 ?
13699                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13700                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13701         }
13702
13703         this.view = new Roo.View(this.list, this.tpl, {
13704             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13705         });
13706         //this.view.wrapEl.setDisplayed(false);
13707         this.view.on('click', this.onViewClick, this);
13708         
13709         
13710         this.store.on('beforeload', this.onBeforeLoad, this);
13711         this.store.on('load', this.onLoad, this);
13712         this.store.on('loadexception', this.onLoadException, this);
13713         /*
13714         if(this.resizable){
13715             this.resizer = new Roo.Resizable(this.list,  {
13716                pinned:true, handles:'se'
13717             });
13718             this.resizer.on('resize', function(r, w, h){
13719                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13720                 this.listWidth = w;
13721                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13722                 this.restrictHeight();
13723             }, this);
13724             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13725         }
13726         */
13727         if(!this.editable){
13728             this.editable = true;
13729             this.setEditable(false);
13730         }
13731         
13732         /*
13733         
13734         if (typeof(this.events.add.listeners) != 'undefined') {
13735             
13736             this.addicon = this.wrap.createChild(
13737                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13738        
13739             this.addicon.on('click', function(e) {
13740                 this.fireEvent('add', this);
13741             }, this);
13742         }
13743         if (typeof(this.events.edit.listeners) != 'undefined') {
13744             
13745             this.editicon = this.wrap.createChild(
13746                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13747             if (this.addicon) {
13748                 this.editicon.setStyle('margin-left', '40px');
13749             }
13750             this.editicon.on('click', function(e) {
13751                 
13752                 // we fire even  if inothing is selected..
13753                 this.fireEvent('edit', this, this.lastData );
13754                 
13755             }, this);
13756         }
13757         */
13758         
13759         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13760             "up" : function(e){
13761                 this.inKeyMode = true;
13762                 this.selectPrev();
13763             },
13764
13765             "down" : function(e){
13766                 if(!this.isExpanded()){
13767                     this.onTriggerClick();
13768                 }else{
13769                     this.inKeyMode = true;
13770                     this.selectNext();
13771                 }
13772             },
13773
13774             "enter" : function(e){
13775 //                this.onViewClick();
13776                 //return true;
13777                 this.collapse();
13778                 
13779                 if(this.fireEvent("specialkey", this, e)){
13780                     this.onViewClick(false);
13781                 }
13782                 
13783                 return true;
13784             },
13785
13786             "esc" : function(e){
13787                 this.collapse();
13788             },
13789
13790             "tab" : function(e){
13791                 this.collapse();
13792                 
13793                 if(this.fireEvent("specialkey", this, e)){
13794                     this.onViewClick(false);
13795                 }
13796                 
13797                 return true;
13798             },
13799
13800             scope : this,
13801
13802             doRelay : function(foo, bar, hname){
13803                 if(hname == 'down' || this.scope.isExpanded()){
13804                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13805                 }
13806                 return true;
13807             },
13808
13809             forceKeyDown: true
13810         });
13811         
13812         
13813         this.queryDelay = Math.max(this.queryDelay || 10,
13814                 this.mode == 'local' ? 10 : 250);
13815         
13816         
13817         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13818         
13819         if(this.typeAhead){
13820             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13821         }
13822         if(this.editable !== false){
13823             this.inputEl().on("keyup", this.onKeyUp, this);
13824         }
13825         if(this.forceSelection){
13826             this.inputEl().on('blur', this.doForce, this);
13827         }
13828         
13829         if(this.multiple){
13830             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13831             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13832         }
13833     },
13834     
13835     initTickableEvents: function()
13836     {   
13837         this.createList();
13838         
13839         if(this.hiddenName){
13840             
13841             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13842             
13843             this.hiddenField.dom.value =
13844                 this.hiddenValue !== undefined ? this.hiddenValue :
13845                 this.value !== undefined ? this.value : '';
13846
13847             // prevent input submission
13848             this.el.dom.removeAttribute('name');
13849             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13850              
13851              
13852         }
13853         
13854 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13855         
13856         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13857         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13858         if(this.triggerList){
13859             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13860         }
13861          
13862         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13863         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13864         
13865         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13866         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13867         
13868         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13869         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13870         
13871         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13872         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13873         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13874         
13875         this.okBtn.hide();
13876         this.cancelBtn.hide();
13877         
13878         var _this = this;
13879         
13880         (function(){
13881             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13882             _this.list.setWidth(lw);
13883         }).defer(100);
13884         
13885         this.list.on('mouseover', this.onViewOver, this);
13886         this.list.on('mousemove', this.onViewMove, this);
13887         
13888         this.list.on('scroll', this.onViewScroll, this);
13889         
13890         if(!this.tpl){
13891             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13892                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13893         }
13894
13895         this.view = new Roo.View(this.list, this.tpl, {
13896             singleSelect:true,
13897             tickable:true,
13898             parent:this,
13899             store: this.store,
13900             selectedClass: this.selectedClass
13901         });
13902         
13903         //this.view.wrapEl.setDisplayed(false);
13904         this.view.on('click', this.onViewClick, this);
13905         
13906         
13907         
13908         this.store.on('beforeload', this.onBeforeLoad, this);
13909         this.store.on('load', this.onLoad, this);
13910         this.store.on('loadexception', this.onLoadException, this);
13911         
13912         if(this.editable){
13913             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13914                 "up" : function(e){
13915                     this.inKeyMode = true;
13916                     this.selectPrev();
13917                 },
13918
13919                 "down" : function(e){
13920                     this.inKeyMode = true;
13921                     this.selectNext();
13922                 },
13923
13924                 "enter" : function(e){
13925                     if(this.fireEvent("specialkey", this, e)){
13926                         this.onViewClick(false);
13927                     }
13928                     
13929                     return true;
13930                 },
13931
13932                 "esc" : function(e){
13933                     this.onTickableFooterButtonClick(e, false, false);
13934                 },
13935
13936                 "tab" : function(e){
13937                     this.fireEvent("specialkey", this, e);
13938                     
13939                     this.onTickableFooterButtonClick(e, false, false);
13940                     
13941                     return true;
13942                 },
13943
13944                 scope : this,
13945
13946                 doRelay : function(e, fn, key){
13947                     if(this.scope.isExpanded()){
13948                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13949                     }
13950                     return true;
13951                 },
13952
13953                 forceKeyDown: true
13954             });
13955         }
13956         
13957         this.queryDelay = Math.max(this.queryDelay || 10,
13958                 this.mode == 'local' ? 10 : 250);
13959         
13960         
13961         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13962         
13963         if(this.typeAhead){
13964             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13965         }
13966         
13967         if(this.editable !== false){
13968             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13969         }
13970         
13971         this.indicator = this.indicatorEl();
13972         
13973         if(this.indicator){
13974             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13975             this.indicator.hide();
13976         }
13977         
13978     },
13979
13980     onDestroy : function(){
13981         if(this.view){
13982             this.view.setStore(null);
13983             this.view.el.removeAllListeners();
13984             this.view.el.remove();
13985             this.view.purgeListeners();
13986         }
13987         if(this.list){
13988             this.list.dom.innerHTML  = '';
13989         }
13990         
13991         if(this.store){
13992             this.store.un('beforeload', this.onBeforeLoad, this);
13993             this.store.un('load', this.onLoad, this);
13994             this.store.un('loadexception', this.onLoadException, this);
13995         }
13996         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13997     },
13998
13999     // private
14000     fireKey : function(e){
14001         if(e.isNavKeyPress() && !this.list.isVisible()){
14002             this.fireEvent("specialkey", this, e);
14003         }
14004     },
14005
14006     // private
14007     onResize: function(w, h){
14008 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14009 //        
14010 //        if(typeof w != 'number'){
14011 //            // we do not handle it!?!?
14012 //            return;
14013 //        }
14014 //        var tw = this.trigger.getWidth();
14015 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14016 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14017 //        var x = w - tw;
14018 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14019 //            
14020 //        //this.trigger.setStyle('left', x+'px');
14021 //        
14022 //        if(this.list && this.listWidth === undefined){
14023 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14024 //            this.list.setWidth(lw);
14025 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14026 //        }
14027         
14028     
14029         
14030     },
14031
14032     /**
14033      * Allow or prevent the user from directly editing the field text.  If false is passed,
14034      * the user will only be able to select from the items defined in the dropdown list.  This method
14035      * is the runtime equivalent of setting the 'editable' config option at config time.
14036      * @param {Boolean} value True to allow the user to directly edit the field text
14037      */
14038     setEditable : function(value){
14039         if(value == this.editable){
14040             return;
14041         }
14042         this.editable = value;
14043         if(!value){
14044             this.inputEl().dom.setAttribute('readOnly', true);
14045             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14046             this.inputEl().addClass('x-combo-noedit');
14047         }else{
14048             this.inputEl().dom.setAttribute('readOnly', false);
14049             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14050             this.inputEl().removeClass('x-combo-noedit');
14051         }
14052     },
14053
14054     // private
14055     
14056     onBeforeLoad : function(combo,opts){
14057         if(!this.hasFocus){
14058             return;
14059         }
14060          if (!opts.add) {
14061             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14062          }
14063         this.restrictHeight();
14064         this.selectedIndex = -1;
14065     },
14066
14067     // private
14068     onLoad : function(){
14069         
14070         this.hasQuery = false;
14071         
14072         if(!this.hasFocus){
14073             return;
14074         }
14075         
14076         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14077             this.loading.hide();
14078         }
14079         
14080         if(this.store.getCount() > 0){
14081             
14082             this.expand();
14083             this.restrictHeight();
14084             if(this.lastQuery == this.allQuery){
14085                 if(this.editable && !this.tickable){
14086                     this.inputEl().dom.select();
14087                 }
14088                 
14089                 if(
14090                     !this.selectByValue(this.value, true) &&
14091                     this.autoFocus && 
14092                     (
14093                         !this.store.lastOptions ||
14094                         typeof(this.store.lastOptions.add) == 'undefined' || 
14095                         this.store.lastOptions.add != true
14096                     )
14097                 ){
14098                     this.select(0, true);
14099                 }
14100             }else{
14101                 if(this.autoFocus){
14102                     this.selectNext();
14103                 }
14104                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14105                     this.taTask.delay(this.typeAheadDelay);
14106                 }
14107             }
14108         }else{
14109             this.onEmptyResults();
14110         }
14111         
14112         //this.el.focus();
14113     },
14114     // private
14115     onLoadException : function()
14116     {
14117         this.hasQuery = false;
14118         
14119         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14120             this.loading.hide();
14121         }
14122         
14123         if(this.tickable && this.editable){
14124             return;
14125         }
14126         
14127         this.collapse();
14128         // only causes errors at present
14129         //Roo.log(this.store.reader.jsonData);
14130         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14131             // fixme
14132             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14133         //}
14134         
14135         
14136     },
14137     // private
14138     onTypeAhead : function(){
14139         if(this.store.getCount() > 0){
14140             var r = this.store.getAt(0);
14141             var newValue = r.data[this.displayField];
14142             var len = newValue.length;
14143             var selStart = this.getRawValue().length;
14144             
14145             if(selStart != len){
14146                 this.setRawValue(newValue);
14147                 this.selectText(selStart, newValue.length);
14148             }
14149         }
14150     },
14151
14152     // private
14153     onSelect : function(record, index){
14154         
14155         if(this.fireEvent('beforeselect', this, record, index) !== false){
14156         
14157             this.setFromData(index > -1 ? record.data : false);
14158             
14159             this.collapse();
14160             this.fireEvent('select', this, record, index);
14161         }
14162     },
14163
14164     /**
14165      * Returns the currently selected field value or empty string if no value is set.
14166      * @return {String} value The selected value
14167      */
14168     getValue : function()
14169     {
14170         if(Roo.isIOS && this.useNativeIOS){
14171             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14172         }
14173         
14174         if(this.multiple){
14175             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14176         }
14177         
14178         if(this.valueField){
14179             return typeof this.value != 'undefined' ? this.value : '';
14180         }else{
14181             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14182         }
14183     },
14184     
14185     getRawValue : function()
14186     {
14187         if(Roo.isIOS && this.useNativeIOS){
14188             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14189         }
14190         
14191         var v = this.inputEl().getValue();
14192         
14193         return v;
14194     },
14195
14196     /**
14197      * Clears any text/value currently set in the field
14198      */
14199     clearValue : function(){
14200         
14201         if(this.hiddenField){
14202             this.hiddenField.dom.value = '';
14203         }
14204         this.value = '';
14205         this.setRawValue('');
14206         this.lastSelectionText = '';
14207         this.lastData = false;
14208         
14209         var close = this.closeTriggerEl();
14210         
14211         if(close){
14212             close.hide();
14213         }
14214         
14215         this.validate();
14216         
14217     },
14218
14219     /**
14220      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14221      * will be displayed in the field.  If the value does not match the data value of an existing item,
14222      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14223      * Otherwise the field will be blank (although the value will still be set).
14224      * @param {String} value The value to match
14225      */
14226     setValue : function(v)
14227     {
14228         if(Roo.isIOS && this.useNativeIOS){
14229             this.setIOSValue(v);
14230             return;
14231         }
14232         
14233         if(this.multiple){
14234             this.syncValue();
14235             return;
14236         }
14237         
14238         var text = v;
14239         if(this.valueField){
14240             var r = this.findRecord(this.valueField, v);
14241             if(r){
14242                 text = r.data[this.displayField];
14243             }else if(this.valueNotFoundText !== undefined){
14244                 text = this.valueNotFoundText;
14245             }
14246         }
14247         this.lastSelectionText = text;
14248         if(this.hiddenField){
14249             this.hiddenField.dom.value = v;
14250         }
14251         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14252         this.value = v;
14253         
14254         var close = this.closeTriggerEl();
14255         
14256         if(close){
14257             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14258         }
14259         
14260         this.validate();
14261     },
14262     /**
14263      * @property {Object} the last set data for the element
14264      */
14265     
14266     lastData : false,
14267     /**
14268      * Sets the value of the field based on a object which is related to the record format for the store.
14269      * @param {Object} value the value to set as. or false on reset?
14270      */
14271     setFromData : function(o){
14272         
14273         if(this.multiple){
14274             this.addItem(o);
14275             return;
14276         }
14277             
14278         var dv = ''; // display value
14279         var vv = ''; // value value..
14280         this.lastData = o;
14281         if (this.displayField) {
14282             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14283         } else {
14284             // this is an error condition!!!
14285             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14286         }
14287         
14288         if(this.valueField){
14289             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14290         }
14291         
14292         var close = this.closeTriggerEl();
14293         
14294         if(close){
14295             if(dv.length || vv * 1 > 0){
14296                 close.show() ;
14297                 this.blockFocus=true;
14298             } else {
14299                 close.hide();
14300             }             
14301         }
14302         
14303         if(this.hiddenField){
14304             this.hiddenField.dom.value = vv;
14305             
14306             this.lastSelectionText = dv;
14307             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14308             this.value = vv;
14309             return;
14310         }
14311         // no hidden field.. - we store the value in 'value', but still display
14312         // display field!!!!
14313         this.lastSelectionText = dv;
14314         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14315         this.value = vv;
14316         
14317         
14318         
14319     },
14320     // private
14321     reset : function(){
14322         // overridden so that last data is reset..
14323         
14324         if(this.multiple){
14325             this.clearItem();
14326             return;
14327         }
14328         
14329         this.setValue(this.originalValue);
14330         //this.clearInvalid();
14331         this.lastData = false;
14332         if (this.view) {
14333             this.view.clearSelections();
14334         }
14335         
14336         this.validate();
14337     },
14338     // private
14339     findRecord : function(prop, value){
14340         var record;
14341         if(this.store.getCount() > 0){
14342             this.store.each(function(r){
14343                 if(r.data[prop] == value){
14344                     record = r;
14345                     return false;
14346                 }
14347                 return true;
14348             });
14349         }
14350         return record;
14351     },
14352     
14353     getName: function()
14354     {
14355         // returns hidden if it's set..
14356         if (!this.rendered) {return ''};
14357         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14358         
14359     },
14360     // private
14361     onViewMove : function(e, t){
14362         this.inKeyMode = false;
14363     },
14364
14365     // private
14366     onViewOver : function(e, t){
14367         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14368             return;
14369         }
14370         var item = this.view.findItemFromChild(t);
14371         
14372         if(item){
14373             var index = this.view.indexOf(item);
14374             this.select(index, false);
14375         }
14376     },
14377
14378     // private
14379     onViewClick : function(view, doFocus, el, e)
14380     {
14381         var index = this.view.getSelectedIndexes()[0];
14382         
14383         var r = this.store.getAt(index);
14384         
14385         if(this.tickable){
14386             
14387             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14388                 return;
14389             }
14390             
14391             var rm = false;
14392             var _this = this;
14393             
14394             Roo.each(this.tickItems, function(v,k){
14395                 
14396                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14397                     Roo.log(v);
14398                     _this.tickItems.splice(k, 1);
14399                     
14400                     if(typeof(e) == 'undefined' && view == false){
14401                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14402                     }
14403                     
14404                     rm = true;
14405                     return;
14406                 }
14407             });
14408             
14409             if(rm){
14410                 return;
14411             }
14412             
14413             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14414                 this.tickItems.push(r.data);
14415             }
14416             
14417             if(typeof(e) == 'undefined' && view == false){
14418                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14419             }
14420                     
14421             return;
14422         }
14423         
14424         if(r){
14425             this.onSelect(r, index);
14426         }
14427         if(doFocus !== false && !this.blockFocus){
14428             this.inputEl().focus();
14429         }
14430     },
14431
14432     // private
14433     restrictHeight : function(){
14434         //this.innerList.dom.style.height = '';
14435         //var inner = this.innerList.dom;
14436         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14437         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14438         //this.list.beginUpdate();
14439         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14440         this.list.alignTo(this.inputEl(), this.listAlign);
14441         this.list.alignTo(this.inputEl(), this.listAlign);
14442         //this.list.endUpdate();
14443     },
14444
14445     // private
14446     onEmptyResults : function(){
14447         
14448         if(this.tickable && this.editable){
14449             this.hasFocus = false;
14450             this.restrictHeight();
14451             return;
14452         }
14453         
14454         this.collapse();
14455     },
14456
14457     /**
14458      * Returns true if the dropdown list is expanded, else false.
14459      */
14460     isExpanded : function(){
14461         return this.list.isVisible();
14462     },
14463
14464     /**
14465      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14466      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14467      * @param {String} value The data value of the item to select
14468      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14469      * selected item if it is not currently in view (defaults to true)
14470      * @return {Boolean} True if the value matched an item in the list, else false
14471      */
14472     selectByValue : function(v, scrollIntoView){
14473         if(v !== undefined && v !== null){
14474             var r = this.findRecord(this.valueField || this.displayField, v);
14475             if(r){
14476                 this.select(this.store.indexOf(r), scrollIntoView);
14477                 return true;
14478             }
14479         }
14480         return false;
14481     },
14482
14483     /**
14484      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14485      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14486      * @param {Number} index The zero-based index of the list item to select
14487      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14488      * selected item if it is not currently in view (defaults to true)
14489      */
14490     select : function(index, scrollIntoView){
14491         this.selectedIndex = index;
14492         this.view.select(index);
14493         if(scrollIntoView !== false){
14494             var el = this.view.getNode(index);
14495             /*
14496              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14497              */
14498             if(el){
14499                 this.list.scrollChildIntoView(el, false);
14500             }
14501         }
14502     },
14503
14504     // private
14505     selectNext : function(){
14506         var ct = this.store.getCount();
14507         if(ct > 0){
14508             if(this.selectedIndex == -1){
14509                 this.select(0);
14510             }else if(this.selectedIndex < ct-1){
14511                 this.select(this.selectedIndex+1);
14512             }
14513         }
14514     },
14515
14516     // private
14517     selectPrev : function(){
14518         var ct = this.store.getCount();
14519         if(ct > 0){
14520             if(this.selectedIndex == -1){
14521                 this.select(0);
14522             }else if(this.selectedIndex != 0){
14523                 this.select(this.selectedIndex-1);
14524             }
14525         }
14526     },
14527
14528     // private
14529     onKeyUp : function(e){
14530         if(this.editable !== false && !e.isSpecialKey()){
14531             this.lastKey = e.getKey();
14532             this.dqTask.delay(this.queryDelay);
14533         }
14534     },
14535
14536     // private
14537     validateBlur : function(){
14538         return !this.list || !this.list.isVisible();   
14539     },
14540
14541     // private
14542     initQuery : function(){
14543         
14544         var v = this.getRawValue();
14545         
14546         if(this.tickable && this.editable){
14547             v = this.tickableInputEl().getValue();
14548         }
14549         
14550         this.doQuery(v);
14551     },
14552
14553     // private
14554     doForce : function(){
14555         if(this.inputEl().dom.value.length > 0){
14556             this.inputEl().dom.value =
14557                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14558              
14559         }
14560     },
14561
14562     /**
14563      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14564      * query allowing the query action to be canceled if needed.
14565      * @param {String} query The SQL query to execute
14566      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14567      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14568      * saved in the current store (defaults to false)
14569      */
14570     doQuery : function(q, forceAll){
14571         
14572         if(q === undefined || q === null){
14573             q = '';
14574         }
14575         var qe = {
14576             query: q,
14577             forceAll: forceAll,
14578             combo: this,
14579             cancel:false
14580         };
14581         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14582             return false;
14583         }
14584         q = qe.query;
14585         
14586         forceAll = qe.forceAll;
14587         if(forceAll === true || (q.length >= this.minChars)){
14588             
14589             this.hasQuery = true;
14590             
14591             if(this.lastQuery != q || this.alwaysQuery){
14592                 this.lastQuery = q;
14593                 if(this.mode == 'local'){
14594                     this.selectedIndex = -1;
14595                     if(forceAll){
14596                         this.store.clearFilter();
14597                     }else{
14598                         
14599                         if(this.specialFilter){
14600                             this.fireEvent('specialfilter', this);
14601                             this.onLoad();
14602                             return;
14603                         }
14604                         
14605                         this.store.filter(this.displayField, q);
14606                     }
14607                     
14608                     this.store.fireEvent("datachanged", this.store);
14609                     
14610                     this.onLoad();
14611                     
14612                     
14613                 }else{
14614                     
14615                     this.store.baseParams[this.queryParam] = q;
14616                     
14617                     var options = {params : this.getParams(q)};
14618                     
14619                     if(this.loadNext){
14620                         options.add = true;
14621                         options.params.start = this.page * this.pageSize;
14622                     }
14623                     
14624                     this.store.load(options);
14625                     
14626                     /*
14627                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14628                      *  we should expand the list on onLoad
14629                      *  so command out it
14630                      */
14631 //                    this.expand();
14632                 }
14633             }else{
14634                 this.selectedIndex = -1;
14635                 this.onLoad();   
14636             }
14637         }
14638         
14639         this.loadNext = false;
14640     },
14641     
14642     // private
14643     getParams : function(q){
14644         var p = {};
14645         //p[this.queryParam] = q;
14646         
14647         if(this.pageSize){
14648             p.start = 0;
14649             p.limit = this.pageSize;
14650         }
14651         return p;
14652     },
14653
14654     /**
14655      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14656      */
14657     collapse : function(){
14658         if(!this.isExpanded()){
14659             return;
14660         }
14661         
14662         this.list.hide();
14663         
14664         this.hasFocus = false;
14665         
14666         if(this.tickable){
14667             this.okBtn.hide();
14668             this.cancelBtn.hide();
14669             this.trigger.show();
14670             
14671             if(this.editable){
14672                 this.tickableInputEl().dom.value = '';
14673                 this.tickableInputEl().blur();
14674             }
14675             
14676         }
14677         
14678         Roo.get(document).un('mousedown', this.collapseIf, this);
14679         Roo.get(document).un('mousewheel', this.collapseIf, this);
14680         if (!this.editable) {
14681             Roo.get(document).un('keydown', this.listKeyPress, this);
14682         }
14683         this.fireEvent('collapse', this);
14684         
14685         this.validate();
14686     },
14687
14688     // private
14689     collapseIf : function(e){
14690         var in_combo  = e.within(this.el);
14691         var in_list =  e.within(this.list);
14692         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14693         
14694         if (in_combo || in_list || is_list) {
14695             //e.stopPropagation();
14696             return;
14697         }
14698         
14699         if(this.tickable){
14700             this.onTickableFooterButtonClick(e, false, false);
14701         }
14702
14703         this.collapse();
14704         
14705     },
14706
14707     /**
14708      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14709      */
14710     expand : function(){
14711        
14712         if(this.isExpanded() || !this.hasFocus){
14713             return;
14714         }
14715         
14716         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14717         this.list.setWidth(lw);
14718         
14719         Roo.log('expand');
14720         
14721         this.list.show();
14722         
14723         this.restrictHeight();
14724         
14725         if(this.tickable){
14726             
14727             this.tickItems = Roo.apply([], this.item);
14728             
14729             this.okBtn.show();
14730             this.cancelBtn.show();
14731             this.trigger.hide();
14732             
14733             if(this.editable){
14734                 this.tickableInputEl().focus();
14735             }
14736             
14737         }
14738         
14739         Roo.get(document).on('mousedown', this.collapseIf, this);
14740         Roo.get(document).on('mousewheel', this.collapseIf, this);
14741         if (!this.editable) {
14742             Roo.get(document).on('keydown', this.listKeyPress, this);
14743         }
14744         
14745         this.fireEvent('expand', this);
14746     },
14747
14748     // private
14749     // Implements the default empty TriggerField.onTriggerClick function
14750     onTriggerClick : function(e)
14751     {
14752         Roo.log('trigger click');
14753         
14754         if(this.disabled || !this.triggerList){
14755             return;
14756         }
14757         
14758         this.page = 0;
14759         this.loadNext = false;
14760         
14761         if(this.isExpanded()){
14762             this.collapse();
14763             if (!this.blockFocus) {
14764                 this.inputEl().focus();
14765             }
14766             
14767         }else {
14768             this.hasFocus = true;
14769             if(this.triggerAction == 'all') {
14770                 this.doQuery(this.allQuery, true);
14771             } else {
14772                 this.doQuery(this.getRawValue());
14773             }
14774             if (!this.blockFocus) {
14775                 this.inputEl().focus();
14776             }
14777         }
14778     },
14779     
14780     onTickableTriggerClick : function(e)
14781     {
14782         if(this.disabled){
14783             return;
14784         }
14785         
14786         this.page = 0;
14787         this.loadNext = false;
14788         this.hasFocus = true;
14789         
14790         if(this.triggerAction == 'all') {
14791             this.doQuery(this.allQuery, true);
14792         } else {
14793             this.doQuery(this.getRawValue());
14794         }
14795     },
14796     
14797     onSearchFieldClick : function(e)
14798     {
14799         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14800             this.onTickableFooterButtonClick(e, false, false);
14801             return;
14802         }
14803         
14804         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14805             return;
14806         }
14807         
14808         this.page = 0;
14809         this.loadNext = false;
14810         this.hasFocus = true;
14811         
14812         if(this.triggerAction == 'all') {
14813             this.doQuery(this.allQuery, true);
14814         } else {
14815             this.doQuery(this.getRawValue());
14816         }
14817     },
14818     
14819     listKeyPress : function(e)
14820     {
14821         //Roo.log('listkeypress');
14822         // scroll to first matching element based on key pres..
14823         if (e.isSpecialKey()) {
14824             return false;
14825         }
14826         var k = String.fromCharCode(e.getKey()).toUpperCase();
14827         //Roo.log(k);
14828         var match  = false;
14829         var csel = this.view.getSelectedNodes();
14830         var cselitem = false;
14831         if (csel.length) {
14832             var ix = this.view.indexOf(csel[0]);
14833             cselitem  = this.store.getAt(ix);
14834             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14835                 cselitem = false;
14836             }
14837             
14838         }
14839         
14840         this.store.each(function(v) { 
14841             if (cselitem) {
14842                 // start at existing selection.
14843                 if (cselitem.id == v.id) {
14844                     cselitem = false;
14845                 }
14846                 return true;
14847             }
14848                 
14849             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14850                 match = this.store.indexOf(v);
14851                 return false;
14852             }
14853             return true;
14854         }, this);
14855         
14856         if (match === false) {
14857             return true; // no more action?
14858         }
14859         // scroll to?
14860         this.view.select(match);
14861         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14862         sn.scrollIntoView(sn.dom.parentNode, false);
14863     },
14864     
14865     onViewScroll : function(e, t){
14866         
14867         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){
14868             return;
14869         }
14870         
14871         this.hasQuery = true;
14872         
14873         this.loading = this.list.select('.loading', true).first();
14874         
14875         if(this.loading === null){
14876             this.list.createChild({
14877                 tag: 'div',
14878                 cls: 'loading roo-select2-more-results roo-select2-active',
14879                 html: 'Loading more results...'
14880             });
14881             
14882             this.loading = this.list.select('.loading', true).first();
14883             
14884             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14885             
14886             this.loading.hide();
14887         }
14888         
14889         this.loading.show();
14890         
14891         var _combo = this;
14892         
14893         this.page++;
14894         this.loadNext = true;
14895         
14896         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14897         
14898         return;
14899     },
14900     
14901     addItem : function(o)
14902     {   
14903         var dv = ''; // display value
14904         
14905         if (this.displayField) {
14906             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14907         } else {
14908             // this is an error condition!!!
14909             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14910         }
14911         
14912         if(!dv.length){
14913             return;
14914         }
14915         
14916         var choice = this.choices.createChild({
14917             tag: 'li',
14918             cls: 'roo-select2-search-choice',
14919             cn: [
14920                 {
14921                     tag: 'div',
14922                     html: dv
14923                 },
14924                 {
14925                     tag: 'a',
14926                     href: '#',
14927                     cls: 'roo-select2-search-choice-close fa fa-times',
14928                     tabindex: '-1'
14929                 }
14930             ]
14931             
14932         }, this.searchField);
14933         
14934         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14935         
14936         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14937         
14938         this.item.push(o);
14939         
14940         this.lastData = o;
14941         
14942         this.syncValue();
14943         
14944         this.inputEl().dom.value = '';
14945         
14946         this.validate();
14947     },
14948     
14949     onRemoveItem : function(e, _self, o)
14950     {
14951         e.preventDefault();
14952         
14953         this.lastItem = Roo.apply([], this.item);
14954         
14955         var index = this.item.indexOf(o.data) * 1;
14956         
14957         if( index < 0){
14958             Roo.log('not this item?!');
14959             return;
14960         }
14961         
14962         this.item.splice(index, 1);
14963         o.item.remove();
14964         
14965         this.syncValue();
14966         
14967         this.fireEvent('remove', this, e);
14968         
14969         this.validate();
14970         
14971     },
14972     
14973     syncValue : function()
14974     {
14975         if(!this.item.length){
14976             this.clearValue();
14977             return;
14978         }
14979             
14980         var value = [];
14981         var _this = this;
14982         Roo.each(this.item, function(i){
14983             if(_this.valueField){
14984                 value.push(i[_this.valueField]);
14985                 return;
14986             }
14987
14988             value.push(i);
14989         });
14990
14991         this.value = value.join(',');
14992
14993         if(this.hiddenField){
14994             this.hiddenField.dom.value = this.value;
14995         }
14996         
14997         this.store.fireEvent("datachanged", this.store);
14998         
14999         this.validate();
15000     },
15001     
15002     clearItem : function()
15003     {
15004         if(!this.multiple){
15005             return;
15006         }
15007         
15008         this.item = [];
15009         
15010         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15011            c.remove();
15012         });
15013         
15014         this.syncValue();
15015         
15016         this.validate();
15017         
15018         if(this.tickable && !Roo.isTouch){
15019             this.view.refresh();
15020         }
15021     },
15022     
15023     inputEl: function ()
15024     {
15025         if(Roo.isIOS && this.useNativeIOS){
15026             return this.el.select('select.roo-ios-select', true).first();
15027         }
15028         
15029         if(Roo.isTouch && this.mobileTouchView){
15030             return this.el.select('input.form-control',true).first();
15031         }
15032         
15033         if(this.tickable){
15034             return this.searchField;
15035         }
15036         
15037         return this.el.select('input.form-control',true).first();
15038     },
15039     
15040     onTickableFooterButtonClick : function(e, btn, el)
15041     {
15042         e.preventDefault();
15043         
15044         this.lastItem = Roo.apply([], this.item);
15045         
15046         if(btn && btn.name == 'cancel'){
15047             this.tickItems = Roo.apply([], this.item);
15048             this.collapse();
15049             return;
15050         }
15051         
15052         this.clearItem();
15053         
15054         var _this = this;
15055         
15056         Roo.each(this.tickItems, function(o){
15057             _this.addItem(o);
15058         });
15059         
15060         this.collapse();
15061         
15062     },
15063     
15064     validate : function()
15065     {
15066         if(this.getVisibilityEl().hasClass('hidden')){
15067             return true;
15068         }
15069         
15070         var v = this.getRawValue();
15071         
15072         if(this.multiple){
15073             v = this.getValue();
15074         }
15075         
15076         if(this.disabled || this.allowBlank || v.length){
15077             this.markValid();
15078             return true;
15079         }
15080         
15081         this.markInvalid();
15082         return false;
15083     },
15084     
15085     tickableInputEl : function()
15086     {
15087         if(!this.tickable || !this.editable){
15088             return this.inputEl();
15089         }
15090         
15091         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15092     },
15093     
15094     
15095     getAutoCreateTouchView : function()
15096     {
15097         var id = Roo.id();
15098         
15099         var cfg = {
15100             cls: 'form-group' //input-group
15101         };
15102         
15103         var input =  {
15104             tag: 'input',
15105             id : id,
15106             type : this.inputType,
15107             cls : 'form-control x-combo-noedit',
15108             autocomplete: 'new-password',
15109             placeholder : this.placeholder || '',
15110             readonly : true
15111         };
15112         
15113         if (this.name) {
15114             input.name = this.name;
15115         }
15116         
15117         if (this.size) {
15118             input.cls += ' input-' + this.size;
15119         }
15120         
15121         if (this.disabled) {
15122             input.disabled = true;
15123         }
15124         
15125         var inputblock = {
15126             cls : '',
15127             cn : [
15128                 input
15129             ]
15130         };
15131         
15132         if(this.before){
15133             inputblock.cls += ' input-group';
15134             
15135             inputblock.cn.unshift({
15136                 tag :'span',
15137                 cls : 'input-group-addon input-group-prepend input-group-text',
15138                 html : this.before
15139             });
15140         }
15141         
15142         if(this.removable && !this.multiple){
15143             inputblock.cls += ' roo-removable';
15144             
15145             inputblock.cn.push({
15146                 tag: 'button',
15147                 html : 'x',
15148                 cls : 'roo-combo-removable-btn close'
15149             });
15150         }
15151
15152         if(this.hasFeedback && !this.allowBlank){
15153             
15154             inputblock.cls += ' has-feedback';
15155             
15156             inputblock.cn.push({
15157                 tag: 'span',
15158                 cls: 'glyphicon form-control-feedback'
15159             });
15160             
15161         }
15162         
15163         if (this.after) {
15164             
15165             inputblock.cls += (this.before) ? '' : ' input-group';
15166             
15167             inputblock.cn.push({
15168                 tag :'span',
15169                 cls : 'input-group-addon input-group-append input-group-text',
15170                 html : this.after
15171             });
15172         }
15173
15174         
15175         var ibwrap = inputblock;
15176         
15177         if(this.multiple){
15178             ibwrap = {
15179                 tag: 'ul',
15180                 cls: 'roo-select2-choices',
15181                 cn:[
15182                     {
15183                         tag: 'li',
15184                         cls: 'roo-select2-search-field',
15185                         cn: [
15186
15187                             inputblock
15188                         ]
15189                     }
15190                 ]
15191             };
15192         
15193             
15194         }
15195         
15196         var combobox = {
15197             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15198             cn: [
15199                 {
15200                     tag: 'input',
15201                     type : 'hidden',
15202                     cls: 'form-hidden-field'
15203                 },
15204                 ibwrap
15205             ]
15206         };
15207         
15208         if(!this.multiple && this.showToggleBtn){
15209             
15210             var caret = {
15211                         tag: 'span',
15212                         cls: 'caret'
15213             };
15214             
15215             if (this.caret != false) {
15216                 caret = {
15217                      tag: 'i',
15218                      cls: 'fa fa-' + this.caret
15219                 };
15220                 
15221             }
15222             
15223             combobox.cn.push({
15224                 tag :'span',
15225                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15226                 cn : [
15227                     caret,
15228                     {
15229                         tag: 'span',
15230                         cls: 'combobox-clear',
15231                         cn  : [
15232                             {
15233                                 tag : 'i',
15234                                 cls: 'icon-remove'
15235                             }
15236                         ]
15237                     }
15238                 ]
15239
15240             })
15241         }
15242         
15243         if(this.multiple){
15244             combobox.cls += ' roo-select2-container-multi';
15245         }
15246         
15247         var align = this.labelAlign || this.parentLabelAlign();
15248         
15249         if (align ==='left' && this.fieldLabel.length) {
15250
15251             cfg.cn = [
15252                 {
15253                    tag : 'i',
15254                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15255                    tooltip : 'This field is required'
15256                 },
15257                 {
15258                     tag: 'label',
15259                     cls : 'control-label col-form-label',
15260                     html : this.fieldLabel
15261
15262                 },
15263                 {
15264                     cls : '', 
15265                     cn: [
15266                         combobox
15267                     ]
15268                 }
15269             ];
15270             
15271             var labelCfg = cfg.cn[1];
15272             var contentCfg = cfg.cn[2];
15273             
15274
15275             if(this.indicatorpos == 'right'){
15276                 cfg.cn = [
15277                     {
15278                         tag: 'label',
15279                         'for' :  id,
15280                         cls : 'control-label col-form-label',
15281                         cn : [
15282                             {
15283                                 tag : 'span',
15284                                 html : this.fieldLabel
15285                             },
15286                             {
15287                                 tag : 'i',
15288                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15289                                 tooltip : 'This field is required'
15290                             }
15291                         ]
15292                     },
15293                     {
15294                         cls : "",
15295                         cn: [
15296                             combobox
15297                         ]
15298                     }
15299
15300                 ];
15301                 
15302                 labelCfg = cfg.cn[0];
15303                 contentCfg = cfg.cn[1];
15304             }
15305             
15306            
15307             
15308             if(this.labelWidth > 12){
15309                 labelCfg.style = "width: " + this.labelWidth + 'px';
15310             }
15311             
15312             if(this.labelWidth < 13 && this.labelmd == 0){
15313                 this.labelmd = this.labelWidth;
15314             }
15315             
15316             if(this.labellg > 0){
15317                 labelCfg.cls += ' col-lg-' + this.labellg;
15318                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15319             }
15320             
15321             if(this.labelmd > 0){
15322                 labelCfg.cls += ' col-md-' + this.labelmd;
15323                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15324             }
15325             
15326             if(this.labelsm > 0){
15327                 labelCfg.cls += ' col-sm-' + this.labelsm;
15328                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15329             }
15330             
15331             if(this.labelxs > 0){
15332                 labelCfg.cls += ' col-xs-' + this.labelxs;
15333                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15334             }
15335                 
15336                 
15337         } else if ( this.fieldLabel.length) {
15338             cfg.cn = [
15339                 {
15340                    tag : 'i',
15341                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15342                    tooltip : 'This field is required'
15343                 },
15344                 {
15345                     tag: 'label',
15346                     cls : 'control-label',
15347                     html : this.fieldLabel
15348
15349                 },
15350                 {
15351                     cls : '', 
15352                     cn: [
15353                         combobox
15354                     ]
15355                 }
15356             ];
15357             
15358             if(this.indicatorpos == 'right'){
15359                 cfg.cn = [
15360                     {
15361                         tag: 'label',
15362                         cls : 'control-label',
15363                         html : this.fieldLabel,
15364                         cn : [
15365                             {
15366                                tag : 'i',
15367                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15368                                tooltip : 'This field is required'
15369                             }
15370                         ]
15371                     },
15372                     {
15373                         cls : '', 
15374                         cn: [
15375                             combobox
15376                         ]
15377                     }
15378                 ];
15379             }
15380         } else {
15381             cfg.cn = combobox;    
15382         }
15383         
15384         
15385         var settings = this;
15386         
15387         ['xs','sm','md','lg'].map(function(size){
15388             if (settings[size]) {
15389                 cfg.cls += ' col-' + size + '-' + settings[size];
15390             }
15391         });
15392         
15393         return cfg;
15394     },
15395     
15396     initTouchView : function()
15397     {
15398         this.renderTouchView();
15399         
15400         this.touchViewEl.on('scroll', function(){
15401             this.el.dom.scrollTop = 0;
15402         }, this);
15403         
15404         this.originalValue = this.getValue();
15405         
15406         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15407         
15408         this.inputEl().on("click", this.showTouchView, this);
15409         if (this.triggerEl) {
15410             this.triggerEl.on("click", this.showTouchView, this);
15411         }
15412         
15413         
15414         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15415         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15416         
15417         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15418         
15419         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15420         this.store.on('load', this.onTouchViewLoad, this);
15421         this.store.on('loadexception', this.onTouchViewLoadException, this);
15422         
15423         if(this.hiddenName){
15424             
15425             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15426             
15427             this.hiddenField.dom.value =
15428                 this.hiddenValue !== undefined ? this.hiddenValue :
15429                 this.value !== undefined ? this.value : '';
15430         
15431             this.el.dom.removeAttribute('name');
15432             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15433         }
15434         
15435         if(this.multiple){
15436             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15437             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15438         }
15439         
15440         if(this.removable && !this.multiple){
15441             var close = this.closeTriggerEl();
15442             if(close){
15443                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15444                 close.on('click', this.removeBtnClick, this, close);
15445             }
15446         }
15447         /*
15448          * fix the bug in Safari iOS8
15449          */
15450         this.inputEl().on("focus", function(e){
15451             document.activeElement.blur();
15452         }, this);
15453         
15454         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15455         
15456         return;
15457         
15458         
15459     },
15460     
15461     renderTouchView : function()
15462     {
15463         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15464         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15465         
15466         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15467         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15468         
15469         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15470         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15471         this.touchViewBodyEl.setStyle('overflow', 'auto');
15472         
15473         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15474         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15475         
15476         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15477         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15478         
15479     },
15480     
15481     showTouchView : function()
15482     {
15483         if(this.disabled){
15484             return;
15485         }
15486         
15487         this.touchViewHeaderEl.hide();
15488
15489         if(this.modalTitle.length){
15490             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15491             this.touchViewHeaderEl.show();
15492         }
15493
15494         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15495         this.touchViewEl.show();
15496
15497         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15498         
15499         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15500         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15501
15502         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15503
15504         if(this.modalTitle.length){
15505             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15506         }
15507         
15508         this.touchViewBodyEl.setHeight(bodyHeight);
15509
15510         if(this.animate){
15511             var _this = this;
15512             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15513         }else{
15514             this.touchViewEl.addClass('in');
15515         }
15516         
15517         if(this._touchViewMask){
15518             Roo.get(document.body).addClass("x-body-masked");
15519             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15520             this._touchViewMask.setStyle('z-index', 10000);
15521             this._touchViewMask.addClass('show');
15522         }
15523         
15524         this.doTouchViewQuery();
15525         
15526     },
15527     
15528     hideTouchView : function()
15529     {
15530         this.touchViewEl.removeClass('in');
15531
15532         if(this.animate){
15533             var _this = this;
15534             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15535         }else{
15536             this.touchViewEl.setStyle('display', 'none');
15537         }
15538         
15539         if(this._touchViewMask){
15540             this._touchViewMask.removeClass('show');
15541             Roo.get(document.body).removeClass("x-body-masked");
15542         }
15543     },
15544     
15545     setTouchViewValue : function()
15546     {
15547         if(this.multiple){
15548             this.clearItem();
15549         
15550             var _this = this;
15551
15552             Roo.each(this.tickItems, function(o){
15553                 this.addItem(o);
15554             }, this);
15555         }
15556         
15557         this.hideTouchView();
15558     },
15559     
15560     doTouchViewQuery : function()
15561     {
15562         var qe = {
15563             query: '',
15564             forceAll: true,
15565             combo: this,
15566             cancel:false
15567         };
15568         
15569         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15570             return false;
15571         }
15572         
15573         if(!this.alwaysQuery || this.mode == 'local'){
15574             this.onTouchViewLoad();
15575             return;
15576         }
15577         
15578         this.store.load();
15579     },
15580     
15581     onTouchViewBeforeLoad : function(combo,opts)
15582     {
15583         return;
15584     },
15585
15586     // private
15587     onTouchViewLoad : function()
15588     {
15589         if(this.store.getCount() < 1){
15590             this.onTouchViewEmptyResults();
15591             return;
15592         }
15593         
15594         this.clearTouchView();
15595         
15596         var rawValue = this.getRawValue();
15597         
15598         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15599         
15600         this.tickItems = [];
15601         
15602         this.store.data.each(function(d, rowIndex){
15603             var row = this.touchViewListGroup.createChild(template);
15604             
15605             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15606                 row.addClass(d.data.cls);
15607             }
15608             
15609             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15610                 var cfg = {
15611                     data : d.data,
15612                     html : d.data[this.displayField]
15613                 };
15614                 
15615                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15616                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15617                 }
15618             }
15619             row.removeClass('selected');
15620             if(!this.multiple && this.valueField &&
15621                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15622             {
15623                 // radio buttons..
15624                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15625                 row.addClass('selected');
15626             }
15627             
15628             if(this.multiple && this.valueField &&
15629                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15630             {
15631                 
15632                 // checkboxes...
15633                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15634                 this.tickItems.push(d.data);
15635             }
15636             
15637             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15638             
15639         }, this);
15640         
15641         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15642         
15643         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15644
15645         if(this.modalTitle.length){
15646             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15647         }
15648
15649         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15650         
15651         if(this.mobile_restrict_height && listHeight < bodyHeight){
15652             this.touchViewBodyEl.setHeight(listHeight);
15653         }
15654         
15655         var _this = this;
15656         
15657         if(firstChecked && listHeight > bodyHeight){
15658             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15659         }
15660         
15661     },
15662     
15663     onTouchViewLoadException : function()
15664     {
15665         this.hideTouchView();
15666     },
15667     
15668     onTouchViewEmptyResults : function()
15669     {
15670         this.clearTouchView();
15671         
15672         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15673         
15674         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15675         
15676     },
15677     
15678     clearTouchView : function()
15679     {
15680         this.touchViewListGroup.dom.innerHTML = '';
15681     },
15682     
15683     onTouchViewClick : function(e, el, o)
15684     {
15685         e.preventDefault();
15686         
15687         var row = o.row;
15688         var rowIndex = o.rowIndex;
15689         
15690         var r = this.store.getAt(rowIndex);
15691         
15692         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15693             
15694             if(!this.multiple){
15695                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15696                     c.dom.removeAttribute('checked');
15697                 }, this);
15698
15699                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15700
15701                 this.setFromData(r.data);
15702
15703                 var close = this.closeTriggerEl();
15704
15705                 if(close){
15706                     close.show();
15707                 }
15708
15709                 this.hideTouchView();
15710
15711                 this.fireEvent('select', this, r, rowIndex);
15712
15713                 return;
15714             }
15715
15716             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15717                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15718                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15719                 return;
15720             }
15721
15722             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15723             this.addItem(r.data);
15724             this.tickItems.push(r.data);
15725         }
15726     },
15727     
15728     getAutoCreateNativeIOS : function()
15729     {
15730         var cfg = {
15731             cls: 'form-group' //input-group,
15732         };
15733         
15734         var combobox =  {
15735             tag: 'select',
15736             cls : 'roo-ios-select'
15737         };
15738         
15739         if (this.name) {
15740             combobox.name = this.name;
15741         }
15742         
15743         if (this.disabled) {
15744             combobox.disabled = true;
15745         }
15746         
15747         var settings = this;
15748         
15749         ['xs','sm','md','lg'].map(function(size){
15750             if (settings[size]) {
15751                 cfg.cls += ' col-' + size + '-' + settings[size];
15752             }
15753         });
15754         
15755         cfg.cn = combobox;
15756         
15757         return cfg;
15758         
15759     },
15760     
15761     initIOSView : function()
15762     {
15763         this.store.on('load', this.onIOSViewLoad, this);
15764         
15765         return;
15766     },
15767     
15768     onIOSViewLoad : function()
15769     {
15770         if(this.store.getCount() < 1){
15771             return;
15772         }
15773         
15774         this.clearIOSView();
15775         
15776         if(this.allowBlank) {
15777             
15778             var default_text = '-- SELECT --';
15779             
15780             if(this.placeholder.length){
15781                 default_text = this.placeholder;
15782             }
15783             
15784             if(this.emptyTitle.length){
15785                 default_text += ' - ' + this.emptyTitle + ' -';
15786             }
15787             
15788             var opt = this.inputEl().createChild({
15789                 tag: 'option',
15790                 value : 0,
15791                 html : default_text
15792             });
15793             
15794             var o = {};
15795             o[this.valueField] = 0;
15796             o[this.displayField] = default_text;
15797             
15798             this.ios_options.push({
15799                 data : o,
15800                 el : opt
15801             });
15802             
15803         }
15804         
15805         this.store.data.each(function(d, rowIndex){
15806             
15807             var html = '';
15808             
15809             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15810                 html = d.data[this.displayField];
15811             }
15812             
15813             var value = '';
15814             
15815             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15816                 value = d.data[this.valueField];
15817             }
15818             
15819             var option = {
15820                 tag: 'option',
15821                 value : value,
15822                 html : html
15823             };
15824             
15825             if(this.value == d.data[this.valueField]){
15826                 option['selected'] = true;
15827             }
15828             
15829             var opt = this.inputEl().createChild(option);
15830             
15831             this.ios_options.push({
15832                 data : d.data,
15833                 el : opt
15834             });
15835             
15836         }, this);
15837         
15838         this.inputEl().on('change', function(){
15839            this.fireEvent('select', this);
15840         }, this);
15841         
15842     },
15843     
15844     clearIOSView: function()
15845     {
15846         this.inputEl().dom.innerHTML = '';
15847         
15848         this.ios_options = [];
15849     },
15850     
15851     setIOSValue: function(v)
15852     {
15853         this.value = v;
15854         
15855         if(!this.ios_options){
15856             return;
15857         }
15858         
15859         Roo.each(this.ios_options, function(opts){
15860            
15861            opts.el.dom.removeAttribute('selected');
15862            
15863            if(opts.data[this.valueField] != v){
15864                return;
15865            }
15866            
15867            opts.el.dom.setAttribute('selected', true);
15868            
15869         }, this);
15870     }
15871
15872     /** 
15873     * @cfg {Boolean} grow 
15874     * @hide 
15875     */
15876     /** 
15877     * @cfg {Number} growMin 
15878     * @hide 
15879     */
15880     /** 
15881     * @cfg {Number} growMax 
15882     * @hide 
15883     */
15884     /**
15885      * @hide
15886      * @method autoSize
15887      */
15888 });
15889
15890 Roo.apply(Roo.bootstrap.ComboBox,  {
15891     
15892     header : {
15893         tag: 'div',
15894         cls: 'modal-header',
15895         cn: [
15896             {
15897                 tag: 'h4',
15898                 cls: 'modal-title'
15899             }
15900         ]
15901     },
15902     
15903     body : {
15904         tag: 'div',
15905         cls: 'modal-body',
15906         cn: [
15907             {
15908                 tag: 'ul',
15909                 cls: 'list-group'
15910             }
15911         ]
15912     },
15913     
15914     listItemRadio : {
15915         tag: 'li',
15916         cls: 'list-group-item',
15917         cn: [
15918             {
15919                 tag: 'span',
15920                 cls: 'roo-combobox-list-group-item-value'
15921             },
15922             {
15923                 tag: 'div',
15924                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15925                 cn: [
15926                     {
15927                         tag: 'input',
15928                         type: 'radio'
15929                     },
15930                     {
15931                         tag: 'label'
15932                     }
15933                 ]
15934             }
15935         ]
15936     },
15937     
15938     listItemCheckbox : {
15939         tag: 'li',
15940         cls: 'list-group-item',
15941         cn: [
15942             {
15943                 tag: 'span',
15944                 cls: 'roo-combobox-list-group-item-value'
15945             },
15946             {
15947                 tag: 'div',
15948                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15949                 cn: [
15950                     {
15951                         tag: 'input',
15952                         type: 'checkbox'
15953                     },
15954                     {
15955                         tag: 'label'
15956                     }
15957                 ]
15958             }
15959         ]
15960     },
15961     
15962     emptyResult : {
15963         tag: 'div',
15964         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15965     },
15966     
15967     footer : {
15968         tag: 'div',
15969         cls: 'modal-footer',
15970         cn: [
15971             {
15972                 tag: 'div',
15973                 cls: 'row',
15974                 cn: [
15975                     {
15976                         tag: 'div',
15977                         cls: 'col-xs-6 text-left',
15978                         cn: {
15979                             tag: 'button',
15980                             cls: 'btn btn-danger roo-touch-view-cancel',
15981                             html: 'Cancel'
15982                         }
15983                     },
15984                     {
15985                         tag: 'div',
15986                         cls: 'col-xs-6 text-right',
15987                         cn: {
15988                             tag: 'button',
15989                             cls: 'btn btn-success roo-touch-view-ok',
15990                             html: 'OK'
15991                         }
15992                     }
15993                 ]
15994             }
15995         ]
15996         
15997     }
15998 });
15999
16000 Roo.apply(Roo.bootstrap.ComboBox,  {
16001     
16002     touchViewTemplate : {
16003         tag: 'div',
16004         cls: 'modal fade roo-combobox-touch-view',
16005         cn: [
16006             {
16007                 tag: 'div',
16008                 cls: 'modal-dialog',
16009                 style : 'position:fixed', // we have to fix position....
16010                 cn: [
16011                     {
16012                         tag: 'div',
16013                         cls: 'modal-content',
16014                         cn: [
16015                             Roo.bootstrap.ComboBox.header,
16016                             Roo.bootstrap.ComboBox.body,
16017                             Roo.bootstrap.ComboBox.footer
16018                         ]
16019                     }
16020                 ]
16021             }
16022         ]
16023     }
16024 });/*
16025  * Based on:
16026  * Ext JS Library 1.1.1
16027  * Copyright(c) 2006-2007, Ext JS, LLC.
16028  *
16029  * Originally Released Under LGPL - original licence link has changed is not relivant.
16030  *
16031  * Fork - LGPL
16032  * <script type="text/javascript">
16033  */
16034
16035 /**
16036  * @class Roo.View
16037  * @extends Roo.util.Observable
16038  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16039  * This class also supports single and multi selection modes. <br>
16040  * Create a data model bound view:
16041  <pre><code>
16042  var store = new Roo.data.Store(...);
16043
16044  var view = new Roo.View({
16045     el : "my-element",
16046     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16047  
16048     singleSelect: true,
16049     selectedClass: "ydataview-selected",
16050     store: store
16051  });
16052
16053  // listen for node click?
16054  view.on("click", function(vw, index, node, e){
16055  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16056  });
16057
16058  // load XML data
16059  dataModel.load("foobar.xml");
16060  </code></pre>
16061  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16062  * <br><br>
16063  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16064  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16065  * 
16066  * Note: old style constructor is still suported (container, template, config)
16067  * 
16068  * @constructor
16069  * Create a new View
16070  * @param {Object} config The config object
16071  * 
16072  */
16073 Roo.View = function(config, depreciated_tpl, depreciated_config){
16074     
16075     this.parent = false;
16076     
16077     if (typeof(depreciated_tpl) == 'undefined') {
16078         // new way.. - universal constructor.
16079         Roo.apply(this, config);
16080         this.el  = Roo.get(this.el);
16081     } else {
16082         // old format..
16083         this.el  = Roo.get(config);
16084         this.tpl = depreciated_tpl;
16085         Roo.apply(this, depreciated_config);
16086     }
16087     this.wrapEl  = this.el.wrap().wrap();
16088     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16089     
16090     
16091     if(typeof(this.tpl) == "string"){
16092         this.tpl = new Roo.Template(this.tpl);
16093     } else {
16094         // support xtype ctors..
16095         this.tpl = new Roo.factory(this.tpl, Roo);
16096     }
16097     
16098     
16099     this.tpl.compile();
16100     
16101     /** @private */
16102     this.addEvents({
16103         /**
16104          * @event beforeclick
16105          * Fires before a click is processed. Returns false to cancel the default action.
16106          * @param {Roo.View} this
16107          * @param {Number} index The index of the target node
16108          * @param {HTMLElement} node The target node
16109          * @param {Roo.EventObject} e The raw event object
16110          */
16111             "beforeclick" : true,
16112         /**
16113          * @event click
16114          * Fires when a template node is clicked.
16115          * @param {Roo.View} this
16116          * @param {Number} index The index of the target node
16117          * @param {HTMLElement} node The target node
16118          * @param {Roo.EventObject} e The raw event object
16119          */
16120             "click" : true,
16121         /**
16122          * @event dblclick
16123          * Fires when a template node is double clicked.
16124          * @param {Roo.View} this
16125          * @param {Number} index The index of the target node
16126          * @param {HTMLElement} node The target node
16127          * @param {Roo.EventObject} e The raw event object
16128          */
16129             "dblclick" : true,
16130         /**
16131          * @event contextmenu
16132          * Fires when a template node is right clicked.
16133          * @param {Roo.View} this
16134          * @param {Number} index The index of the target node
16135          * @param {HTMLElement} node The target node
16136          * @param {Roo.EventObject} e The raw event object
16137          */
16138             "contextmenu" : true,
16139         /**
16140          * @event selectionchange
16141          * Fires when the selected nodes change.
16142          * @param {Roo.View} this
16143          * @param {Array} selections Array of the selected nodes
16144          */
16145             "selectionchange" : true,
16146     
16147         /**
16148          * @event beforeselect
16149          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16150          * @param {Roo.View} this
16151          * @param {HTMLElement} node The node to be selected
16152          * @param {Array} selections Array of currently selected nodes
16153          */
16154             "beforeselect" : true,
16155         /**
16156          * @event preparedata
16157          * Fires on every row to render, to allow you to change the data.
16158          * @param {Roo.View} this
16159          * @param {Object} data to be rendered (change this)
16160          */
16161           "preparedata" : true
16162           
16163           
16164         });
16165
16166
16167
16168     this.el.on({
16169         "click": this.onClick,
16170         "dblclick": this.onDblClick,
16171         "contextmenu": this.onContextMenu,
16172         scope:this
16173     });
16174
16175     this.selections = [];
16176     this.nodes = [];
16177     this.cmp = new Roo.CompositeElementLite([]);
16178     if(this.store){
16179         this.store = Roo.factory(this.store, Roo.data);
16180         this.setStore(this.store, true);
16181     }
16182     
16183     if ( this.footer && this.footer.xtype) {
16184            
16185          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16186         
16187         this.footer.dataSource = this.store;
16188         this.footer.container = fctr;
16189         this.footer = Roo.factory(this.footer, Roo);
16190         fctr.insertFirst(this.el);
16191         
16192         // this is a bit insane - as the paging toolbar seems to detach the el..
16193 //        dom.parentNode.parentNode.parentNode
16194          // they get detached?
16195     }
16196     
16197     
16198     Roo.View.superclass.constructor.call(this);
16199     
16200     
16201 };
16202
16203 Roo.extend(Roo.View, Roo.util.Observable, {
16204     
16205      /**
16206      * @cfg {Roo.data.Store} store Data store to load data from.
16207      */
16208     store : false,
16209     
16210     /**
16211      * @cfg {String|Roo.Element} el The container element.
16212      */
16213     el : '',
16214     
16215     /**
16216      * @cfg {String|Roo.Template} tpl The template used by this View 
16217      */
16218     tpl : false,
16219     /**
16220      * @cfg {String} dataName the named area of the template to use as the data area
16221      *                          Works with domtemplates roo-name="name"
16222      */
16223     dataName: false,
16224     /**
16225      * @cfg {String} selectedClass The css class to add to selected nodes
16226      */
16227     selectedClass : "x-view-selected",
16228      /**
16229      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16230      */
16231     emptyText : "",
16232     
16233     /**
16234      * @cfg {String} text to display on mask (default Loading)
16235      */
16236     mask : false,
16237     /**
16238      * @cfg {Boolean} multiSelect Allow multiple selection
16239      */
16240     multiSelect : false,
16241     /**
16242      * @cfg {Boolean} singleSelect Allow single selection
16243      */
16244     singleSelect:  false,
16245     
16246     /**
16247      * @cfg {Boolean} toggleSelect - selecting 
16248      */
16249     toggleSelect : false,
16250     
16251     /**
16252      * @cfg {Boolean} tickable - selecting 
16253      */
16254     tickable : false,
16255     
16256     /**
16257      * Returns the element this view is bound to.
16258      * @return {Roo.Element}
16259      */
16260     getEl : function(){
16261         return this.wrapEl;
16262     },
16263     
16264     
16265
16266     /**
16267      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16268      */
16269     refresh : function(){
16270         //Roo.log('refresh');
16271         var t = this.tpl;
16272         
16273         // if we are using something like 'domtemplate', then
16274         // the what gets used is:
16275         // t.applySubtemplate(NAME, data, wrapping data..)
16276         // the outer template then get' applied with
16277         //     the store 'extra data'
16278         // and the body get's added to the
16279         //      roo-name="data" node?
16280         //      <span class='roo-tpl-{name}'></span> ?????
16281         
16282         
16283         
16284         this.clearSelections();
16285         this.el.update("");
16286         var html = [];
16287         var records = this.store.getRange();
16288         if(records.length < 1) {
16289             
16290             // is this valid??  = should it render a template??
16291             
16292             this.el.update(this.emptyText);
16293             return;
16294         }
16295         var el = this.el;
16296         if (this.dataName) {
16297             this.el.update(t.apply(this.store.meta)); //????
16298             el = this.el.child('.roo-tpl-' + this.dataName);
16299         }
16300         
16301         for(var i = 0, len = records.length; i < len; i++){
16302             var data = this.prepareData(records[i].data, i, records[i]);
16303             this.fireEvent("preparedata", this, data, i, records[i]);
16304             
16305             var d = Roo.apply({}, data);
16306             
16307             if(this.tickable){
16308                 Roo.apply(d, {'roo-id' : Roo.id()});
16309                 
16310                 var _this = this;
16311             
16312                 Roo.each(this.parent.item, function(item){
16313                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16314                         return;
16315                     }
16316                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16317                 });
16318             }
16319             
16320             html[html.length] = Roo.util.Format.trim(
16321                 this.dataName ?
16322                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16323                     t.apply(d)
16324             );
16325         }
16326         
16327         
16328         
16329         el.update(html.join(""));
16330         this.nodes = el.dom.childNodes;
16331         this.updateIndexes(0);
16332     },
16333     
16334
16335     /**
16336      * Function to override to reformat the data that is sent to
16337      * the template for each node.
16338      * DEPRICATED - use the preparedata event handler.
16339      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16340      * a JSON object for an UpdateManager bound view).
16341      */
16342     prepareData : function(data, index, record)
16343     {
16344         this.fireEvent("preparedata", this, data, index, record);
16345         return data;
16346     },
16347
16348     onUpdate : function(ds, record){
16349         // Roo.log('on update');   
16350         this.clearSelections();
16351         var index = this.store.indexOf(record);
16352         var n = this.nodes[index];
16353         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16354         n.parentNode.removeChild(n);
16355         this.updateIndexes(index, index);
16356     },
16357
16358     
16359     
16360 // --------- FIXME     
16361     onAdd : function(ds, records, index)
16362     {
16363         //Roo.log(['on Add', ds, records, index] );        
16364         this.clearSelections();
16365         if(this.nodes.length == 0){
16366             this.refresh();
16367             return;
16368         }
16369         var n = this.nodes[index];
16370         for(var i = 0, len = records.length; i < len; i++){
16371             var d = this.prepareData(records[i].data, i, records[i]);
16372             if(n){
16373                 this.tpl.insertBefore(n, d);
16374             }else{
16375                 
16376                 this.tpl.append(this.el, d);
16377             }
16378         }
16379         this.updateIndexes(index);
16380     },
16381
16382     onRemove : function(ds, record, index){
16383        // Roo.log('onRemove');
16384         this.clearSelections();
16385         var el = this.dataName  ?
16386             this.el.child('.roo-tpl-' + this.dataName) :
16387             this.el; 
16388         
16389         el.dom.removeChild(this.nodes[index]);
16390         this.updateIndexes(index);
16391     },
16392
16393     /**
16394      * Refresh an individual node.
16395      * @param {Number} index
16396      */
16397     refreshNode : function(index){
16398         this.onUpdate(this.store, this.store.getAt(index));
16399     },
16400
16401     updateIndexes : function(startIndex, endIndex){
16402         var ns = this.nodes;
16403         startIndex = startIndex || 0;
16404         endIndex = endIndex || ns.length - 1;
16405         for(var i = startIndex; i <= endIndex; i++){
16406             ns[i].nodeIndex = i;
16407         }
16408     },
16409
16410     /**
16411      * Changes the data store this view uses and refresh the view.
16412      * @param {Store} store
16413      */
16414     setStore : function(store, initial){
16415         if(!initial && this.store){
16416             this.store.un("datachanged", this.refresh);
16417             this.store.un("add", this.onAdd);
16418             this.store.un("remove", this.onRemove);
16419             this.store.un("update", this.onUpdate);
16420             this.store.un("clear", this.refresh);
16421             this.store.un("beforeload", this.onBeforeLoad);
16422             this.store.un("load", this.onLoad);
16423             this.store.un("loadexception", this.onLoad);
16424         }
16425         if(store){
16426           
16427             store.on("datachanged", this.refresh, this);
16428             store.on("add", this.onAdd, this);
16429             store.on("remove", this.onRemove, this);
16430             store.on("update", this.onUpdate, this);
16431             store.on("clear", this.refresh, this);
16432             store.on("beforeload", this.onBeforeLoad, this);
16433             store.on("load", this.onLoad, this);
16434             store.on("loadexception", this.onLoad, this);
16435         }
16436         
16437         if(store){
16438             this.refresh();
16439         }
16440     },
16441     /**
16442      * onbeforeLoad - masks the loading area.
16443      *
16444      */
16445     onBeforeLoad : function(store,opts)
16446     {
16447          //Roo.log('onBeforeLoad');   
16448         if (!opts.add) {
16449             this.el.update("");
16450         }
16451         this.el.mask(this.mask ? this.mask : "Loading" ); 
16452     },
16453     onLoad : function ()
16454     {
16455         this.el.unmask();
16456     },
16457     
16458
16459     /**
16460      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16461      * @param {HTMLElement} node
16462      * @return {HTMLElement} The template node
16463      */
16464     findItemFromChild : function(node){
16465         var el = this.dataName  ?
16466             this.el.child('.roo-tpl-' + this.dataName,true) :
16467             this.el.dom; 
16468         
16469         if(!node || node.parentNode == el){
16470                     return node;
16471             }
16472             var p = node.parentNode;
16473             while(p && p != el){
16474             if(p.parentNode == el){
16475                 return p;
16476             }
16477             p = p.parentNode;
16478         }
16479             return null;
16480     },
16481
16482     /** @ignore */
16483     onClick : function(e){
16484         var item = this.findItemFromChild(e.getTarget());
16485         if(item){
16486             var index = this.indexOf(item);
16487             if(this.onItemClick(item, index, e) !== false){
16488                 this.fireEvent("click", this, index, item, e);
16489             }
16490         }else{
16491             this.clearSelections();
16492         }
16493     },
16494
16495     /** @ignore */
16496     onContextMenu : function(e){
16497         var item = this.findItemFromChild(e.getTarget());
16498         if(item){
16499             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16500         }
16501     },
16502
16503     /** @ignore */
16504     onDblClick : function(e){
16505         var item = this.findItemFromChild(e.getTarget());
16506         if(item){
16507             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16508         }
16509     },
16510
16511     onItemClick : function(item, index, e)
16512     {
16513         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16514             return false;
16515         }
16516         if (this.toggleSelect) {
16517             var m = this.isSelected(item) ? 'unselect' : 'select';
16518             //Roo.log(m);
16519             var _t = this;
16520             _t[m](item, true, false);
16521             return true;
16522         }
16523         if(this.multiSelect || this.singleSelect){
16524             if(this.multiSelect && e.shiftKey && this.lastSelection){
16525                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16526             }else{
16527                 this.select(item, this.multiSelect && e.ctrlKey);
16528                 this.lastSelection = item;
16529             }
16530             
16531             if(!this.tickable){
16532                 e.preventDefault();
16533             }
16534             
16535         }
16536         return true;
16537     },
16538
16539     /**
16540      * Get the number of selected nodes.
16541      * @return {Number}
16542      */
16543     getSelectionCount : function(){
16544         return this.selections.length;
16545     },
16546
16547     /**
16548      * Get the currently selected nodes.
16549      * @return {Array} An array of HTMLElements
16550      */
16551     getSelectedNodes : function(){
16552         return this.selections;
16553     },
16554
16555     /**
16556      * Get the indexes of the selected nodes.
16557      * @return {Array}
16558      */
16559     getSelectedIndexes : function(){
16560         var indexes = [], s = this.selections;
16561         for(var i = 0, len = s.length; i < len; i++){
16562             indexes.push(s[i].nodeIndex);
16563         }
16564         return indexes;
16565     },
16566
16567     /**
16568      * Clear all selections
16569      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16570      */
16571     clearSelections : function(suppressEvent){
16572         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16573             this.cmp.elements = this.selections;
16574             this.cmp.removeClass(this.selectedClass);
16575             this.selections = [];
16576             if(!suppressEvent){
16577                 this.fireEvent("selectionchange", this, this.selections);
16578             }
16579         }
16580     },
16581
16582     /**
16583      * Returns true if the passed node is selected
16584      * @param {HTMLElement/Number} node The node or node index
16585      * @return {Boolean}
16586      */
16587     isSelected : function(node){
16588         var s = this.selections;
16589         if(s.length < 1){
16590             return false;
16591         }
16592         node = this.getNode(node);
16593         return s.indexOf(node) !== -1;
16594     },
16595
16596     /**
16597      * Selects nodes.
16598      * @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
16599      * @param {Boolean} keepExisting (optional) true to keep existing selections
16600      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16601      */
16602     select : function(nodeInfo, keepExisting, suppressEvent){
16603         if(nodeInfo instanceof Array){
16604             if(!keepExisting){
16605                 this.clearSelections(true);
16606             }
16607             for(var i = 0, len = nodeInfo.length; i < len; i++){
16608                 this.select(nodeInfo[i], true, true);
16609             }
16610             return;
16611         } 
16612         var node = this.getNode(nodeInfo);
16613         if(!node || this.isSelected(node)){
16614             return; // already selected.
16615         }
16616         if(!keepExisting){
16617             this.clearSelections(true);
16618         }
16619         
16620         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16621             Roo.fly(node).addClass(this.selectedClass);
16622             this.selections.push(node);
16623             if(!suppressEvent){
16624                 this.fireEvent("selectionchange", this, this.selections);
16625             }
16626         }
16627         
16628         
16629     },
16630       /**
16631      * Unselects nodes.
16632      * @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
16633      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16634      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16635      */
16636     unselect : function(nodeInfo, keepExisting, suppressEvent)
16637     {
16638         if(nodeInfo instanceof Array){
16639             Roo.each(this.selections, function(s) {
16640                 this.unselect(s, nodeInfo);
16641             }, this);
16642             return;
16643         }
16644         var node = this.getNode(nodeInfo);
16645         if(!node || !this.isSelected(node)){
16646             //Roo.log("not selected");
16647             return; // not selected.
16648         }
16649         // fireevent???
16650         var ns = [];
16651         Roo.each(this.selections, function(s) {
16652             if (s == node ) {
16653                 Roo.fly(node).removeClass(this.selectedClass);
16654
16655                 return;
16656             }
16657             ns.push(s);
16658         },this);
16659         
16660         this.selections= ns;
16661         this.fireEvent("selectionchange", this, this.selections);
16662     },
16663
16664     /**
16665      * Gets a template node.
16666      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16667      * @return {HTMLElement} The node or null if it wasn't found
16668      */
16669     getNode : function(nodeInfo){
16670         if(typeof nodeInfo == "string"){
16671             return document.getElementById(nodeInfo);
16672         }else if(typeof nodeInfo == "number"){
16673             return this.nodes[nodeInfo];
16674         }
16675         return nodeInfo;
16676     },
16677
16678     /**
16679      * Gets a range template nodes.
16680      * @param {Number} startIndex
16681      * @param {Number} endIndex
16682      * @return {Array} An array of nodes
16683      */
16684     getNodes : function(start, end){
16685         var ns = this.nodes;
16686         start = start || 0;
16687         end = typeof end == "undefined" ? ns.length - 1 : end;
16688         var nodes = [];
16689         if(start <= end){
16690             for(var i = start; i <= end; i++){
16691                 nodes.push(ns[i]);
16692             }
16693         } else{
16694             for(var i = start; i >= end; i--){
16695                 nodes.push(ns[i]);
16696             }
16697         }
16698         return nodes;
16699     },
16700
16701     /**
16702      * Finds the index of the passed node
16703      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16704      * @return {Number} The index of the node or -1
16705      */
16706     indexOf : function(node){
16707         node = this.getNode(node);
16708         if(typeof node.nodeIndex == "number"){
16709             return node.nodeIndex;
16710         }
16711         var ns = this.nodes;
16712         for(var i = 0, len = ns.length; i < len; i++){
16713             if(ns[i] == node){
16714                 return i;
16715             }
16716         }
16717         return -1;
16718     }
16719 });
16720 /*
16721  * - LGPL
16722  *
16723  * based on jquery fullcalendar
16724  * 
16725  */
16726
16727 Roo.bootstrap = Roo.bootstrap || {};
16728 /**
16729  * @class Roo.bootstrap.Calendar
16730  * @extends Roo.bootstrap.Component
16731  * Bootstrap Calendar class
16732  * @cfg {Boolean} loadMask (true|false) default false
16733  * @cfg {Object} header generate the user specific header of the calendar, default false
16734
16735  * @constructor
16736  * Create a new Container
16737  * @param {Object} config The config object
16738  */
16739
16740
16741
16742 Roo.bootstrap.Calendar = function(config){
16743     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16744      this.addEvents({
16745         /**
16746              * @event select
16747              * Fires when a date is selected
16748              * @param {DatePicker} this
16749              * @param {Date} date The selected date
16750              */
16751         'select': true,
16752         /**
16753              * @event monthchange
16754              * Fires when the displayed month changes 
16755              * @param {DatePicker} this
16756              * @param {Date} date The selected month
16757              */
16758         'monthchange': true,
16759         /**
16760              * @event evententer
16761              * Fires when mouse over an event
16762              * @param {Calendar} this
16763              * @param {event} Event
16764              */
16765         'evententer': true,
16766         /**
16767              * @event eventleave
16768              * Fires when the mouse leaves an
16769              * @param {Calendar} this
16770              * @param {event}
16771              */
16772         'eventleave': true,
16773         /**
16774              * @event eventclick
16775              * Fires when the mouse click an
16776              * @param {Calendar} this
16777              * @param {event}
16778              */
16779         'eventclick': true
16780         
16781     });
16782
16783 };
16784
16785 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16786     
16787      /**
16788      * @cfg {Number} startDay
16789      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16790      */
16791     startDay : 0,
16792     
16793     loadMask : false,
16794     
16795     header : false,
16796       
16797     getAutoCreate : function(){
16798         
16799         
16800         var fc_button = function(name, corner, style, content ) {
16801             return Roo.apply({},{
16802                 tag : 'span',
16803                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16804                          (corner.length ?
16805                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16806                             ''
16807                         ),
16808                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16809                 unselectable: 'on'
16810             });
16811         };
16812         
16813         var header = {};
16814         
16815         if(!this.header){
16816             header = {
16817                 tag : 'table',
16818                 cls : 'fc-header',
16819                 style : 'width:100%',
16820                 cn : [
16821                     {
16822                         tag: 'tr',
16823                         cn : [
16824                             {
16825                                 tag : 'td',
16826                                 cls : 'fc-header-left',
16827                                 cn : [
16828                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16829                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16830                                     { tag: 'span', cls: 'fc-header-space' },
16831                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16832
16833
16834                                 ]
16835                             },
16836
16837                             {
16838                                 tag : 'td',
16839                                 cls : 'fc-header-center',
16840                                 cn : [
16841                                     {
16842                                         tag: 'span',
16843                                         cls: 'fc-header-title',
16844                                         cn : {
16845                                             tag: 'H2',
16846                                             html : 'month / year'
16847                                         }
16848                                     }
16849
16850                                 ]
16851                             },
16852                             {
16853                                 tag : 'td',
16854                                 cls : 'fc-header-right',
16855                                 cn : [
16856                               /*      fc_button('month', 'left', '', 'month' ),
16857                                     fc_button('week', '', '', 'week' ),
16858                                     fc_button('day', 'right', '', 'day' )
16859                                 */    
16860
16861                                 ]
16862                             }
16863
16864                         ]
16865                     }
16866                 ]
16867             };
16868         }
16869         
16870         header = this.header;
16871         
16872        
16873         var cal_heads = function() {
16874             var ret = [];
16875             // fixme - handle this.
16876             
16877             for (var i =0; i < Date.dayNames.length; i++) {
16878                 var d = Date.dayNames[i];
16879                 ret.push({
16880                     tag: 'th',
16881                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16882                     html : d.substring(0,3)
16883                 });
16884                 
16885             }
16886             ret[0].cls += ' fc-first';
16887             ret[6].cls += ' fc-last';
16888             return ret;
16889         };
16890         var cal_cell = function(n) {
16891             return  {
16892                 tag: 'td',
16893                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16894                 cn : [
16895                     {
16896                         cn : [
16897                             {
16898                                 cls: 'fc-day-number',
16899                                 html: 'D'
16900                             },
16901                             {
16902                                 cls: 'fc-day-content',
16903                              
16904                                 cn : [
16905                                      {
16906                                         style: 'position: relative;' // height: 17px;
16907                                     }
16908                                 ]
16909                             }
16910                             
16911                             
16912                         ]
16913                     }
16914                 ]
16915                 
16916             }
16917         };
16918         var cal_rows = function() {
16919             
16920             var ret = [];
16921             for (var r = 0; r < 6; r++) {
16922                 var row= {
16923                     tag : 'tr',
16924                     cls : 'fc-week',
16925                     cn : []
16926                 };
16927                 
16928                 for (var i =0; i < Date.dayNames.length; i++) {
16929                     var d = Date.dayNames[i];
16930                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16931
16932                 }
16933                 row.cn[0].cls+=' fc-first';
16934                 row.cn[0].cn[0].style = 'min-height:90px';
16935                 row.cn[6].cls+=' fc-last';
16936                 ret.push(row);
16937                 
16938             }
16939             ret[0].cls += ' fc-first';
16940             ret[4].cls += ' fc-prev-last';
16941             ret[5].cls += ' fc-last';
16942             return ret;
16943             
16944         };
16945         
16946         var cal_table = {
16947             tag: 'table',
16948             cls: 'fc-border-separate',
16949             style : 'width:100%',
16950             cellspacing  : 0,
16951             cn : [
16952                 { 
16953                     tag: 'thead',
16954                     cn : [
16955                         { 
16956                             tag: 'tr',
16957                             cls : 'fc-first fc-last',
16958                             cn : cal_heads()
16959                         }
16960                     ]
16961                 },
16962                 { 
16963                     tag: 'tbody',
16964                     cn : cal_rows()
16965                 }
16966                   
16967             ]
16968         };
16969          
16970          var cfg = {
16971             cls : 'fc fc-ltr',
16972             cn : [
16973                 header,
16974                 {
16975                     cls : 'fc-content',
16976                     style : "position: relative;",
16977                     cn : [
16978                         {
16979                             cls : 'fc-view fc-view-month fc-grid',
16980                             style : 'position: relative',
16981                             unselectable : 'on',
16982                             cn : [
16983                                 {
16984                                     cls : 'fc-event-container',
16985                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16986                                 },
16987                                 cal_table
16988                             ]
16989                         }
16990                     ]
16991     
16992                 }
16993            ] 
16994             
16995         };
16996         
16997          
16998         
16999         return cfg;
17000     },
17001     
17002     
17003     initEvents : function()
17004     {
17005         if(!this.store){
17006             throw "can not find store for calendar";
17007         }
17008         
17009         var mark = {
17010             tag: "div",
17011             cls:"x-dlg-mask",
17012             style: "text-align:center",
17013             cn: [
17014                 {
17015                     tag: "div",
17016                     style: "background-color:white;width:50%;margin:250 auto",
17017                     cn: [
17018                         {
17019                             tag: "img",
17020                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17021                         },
17022                         {
17023                             tag: "span",
17024                             html: "Loading"
17025                         }
17026                         
17027                     ]
17028                 }
17029             ]
17030         };
17031         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17032         
17033         var size = this.el.select('.fc-content', true).first().getSize();
17034         this.maskEl.setSize(size.width, size.height);
17035         this.maskEl.enableDisplayMode("block");
17036         if(!this.loadMask){
17037             this.maskEl.hide();
17038         }
17039         
17040         this.store = Roo.factory(this.store, Roo.data);
17041         this.store.on('load', this.onLoad, this);
17042         this.store.on('beforeload', this.onBeforeLoad, this);
17043         
17044         this.resize();
17045         
17046         this.cells = this.el.select('.fc-day',true);
17047         //Roo.log(this.cells);
17048         this.textNodes = this.el.query('.fc-day-number');
17049         this.cells.addClassOnOver('fc-state-hover');
17050         
17051         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17052         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17053         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17054         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17055         
17056         this.on('monthchange', this.onMonthChange, this);
17057         
17058         this.update(new Date().clearTime());
17059     },
17060     
17061     resize : function() {
17062         var sz  = this.el.getSize();
17063         
17064         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17065         this.el.select('.fc-day-content div',true).setHeight(34);
17066     },
17067     
17068     
17069     // private
17070     showPrevMonth : function(e){
17071         this.update(this.activeDate.add("mo", -1));
17072     },
17073     showToday : function(e){
17074         this.update(new Date().clearTime());
17075     },
17076     // private
17077     showNextMonth : function(e){
17078         this.update(this.activeDate.add("mo", 1));
17079     },
17080
17081     // private
17082     showPrevYear : function(){
17083         this.update(this.activeDate.add("y", -1));
17084     },
17085
17086     // private
17087     showNextYear : function(){
17088         this.update(this.activeDate.add("y", 1));
17089     },
17090
17091     
17092    // private
17093     update : function(date)
17094     {
17095         var vd = this.activeDate;
17096         this.activeDate = date;
17097 //        if(vd && this.el){
17098 //            var t = date.getTime();
17099 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17100 //                Roo.log('using add remove');
17101 //                
17102 //                this.fireEvent('monthchange', this, date);
17103 //                
17104 //                this.cells.removeClass("fc-state-highlight");
17105 //                this.cells.each(function(c){
17106 //                   if(c.dateValue == t){
17107 //                       c.addClass("fc-state-highlight");
17108 //                       setTimeout(function(){
17109 //                            try{c.dom.firstChild.focus();}catch(e){}
17110 //                       }, 50);
17111 //                       return false;
17112 //                   }
17113 //                   return true;
17114 //                });
17115 //                return;
17116 //            }
17117 //        }
17118         
17119         var days = date.getDaysInMonth();
17120         
17121         var firstOfMonth = date.getFirstDateOfMonth();
17122         var startingPos = firstOfMonth.getDay()-this.startDay;
17123         
17124         if(startingPos < this.startDay){
17125             startingPos += 7;
17126         }
17127         
17128         var pm = date.add(Date.MONTH, -1);
17129         var prevStart = pm.getDaysInMonth()-startingPos;
17130 //        
17131         this.cells = this.el.select('.fc-day',true);
17132         this.textNodes = this.el.query('.fc-day-number');
17133         this.cells.addClassOnOver('fc-state-hover');
17134         
17135         var cells = this.cells.elements;
17136         var textEls = this.textNodes;
17137         
17138         Roo.each(cells, function(cell){
17139             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17140         });
17141         
17142         days += startingPos;
17143
17144         // convert everything to numbers so it's fast
17145         var day = 86400000;
17146         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17147         //Roo.log(d);
17148         //Roo.log(pm);
17149         //Roo.log(prevStart);
17150         
17151         var today = new Date().clearTime().getTime();
17152         var sel = date.clearTime().getTime();
17153         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17154         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17155         var ddMatch = this.disabledDatesRE;
17156         var ddText = this.disabledDatesText;
17157         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17158         var ddaysText = this.disabledDaysText;
17159         var format = this.format;
17160         
17161         var setCellClass = function(cal, cell){
17162             cell.row = 0;
17163             cell.events = [];
17164             cell.more = [];
17165             //Roo.log('set Cell Class');
17166             cell.title = "";
17167             var t = d.getTime();
17168             
17169             //Roo.log(d);
17170             
17171             cell.dateValue = t;
17172             if(t == today){
17173                 cell.className += " fc-today";
17174                 cell.className += " fc-state-highlight";
17175                 cell.title = cal.todayText;
17176             }
17177             if(t == sel){
17178                 // disable highlight in other month..
17179                 //cell.className += " fc-state-highlight";
17180                 
17181             }
17182             // disabling
17183             if(t < min) {
17184                 cell.className = " fc-state-disabled";
17185                 cell.title = cal.minText;
17186                 return;
17187             }
17188             if(t > max) {
17189                 cell.className = " fc-state-disabled";
17190                 cell.title = cal.maxText;
17191                 return;
17192             }
17193             if(ddays){
17194                 if(ddays.indexOf(d.getDay()) != -1){
17195                     cell.title = ddaysText;
17196                     cell.className = " fc-state-disabled";
17197                 }
17198             }
17199             if(ddMatch && format){
17200                 var fvalue = d.dateFormat(format);
17201                 if(ddMatch.test(fvalue)){
17202                     cell.title = ddText.replace("%0", fvalue);
17203                     cell.className = " fc-state-disabled";
17204                 }
17205             }
17206             
17207             if (!cell.initialClassName) {
17208                 cell.initialClassName = cell.dom.className;
17209             }
17210             
17211             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17212         };
17213
17214         var i = 0;
17215         
17216         for(; i < startingPos; i++) {
17217             textEls[i].innerHTML = (++prevStart);
17218             d.setDate(d.getDate()+1);
17219             
17220             cells[i].className = "fc-past fc-other-month";
17221             setCellClass(this, cells[i]);
17222         }
17223         
17224         var intDay = 0;
17225         
17226         for(; i < days; i++){
17227             intDay = i - startingPos + 1;
17228             textEls[i].innerHTML = (intDay);
17229             d.setDate(d.getDate()+1);
17230             
17231             cells[i].className = ''; // "x-date-active";
17232             setCellClass(this, cells[i]);
17233         }
17234         var extraDays = 0;
17235         
17236         for(; i < 42; i++) {
17237             textEls[i].innerHTML = (++extraDays);
17238             d.setDate(d.getDate()+1);
17239             
17240             cells[i].className = "fc-future fc-other-month";
17241             setCellClass(this, cells[i]);
17242         }
17243         
17244         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17245         
17246         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17247         
17248         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17249         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17250         
17251         if(totalRows != 6){
17252             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17253             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17254         }
17255         
17256         this.fireEvent('monthchange', this, date);
17257         
17258         
17259         /*
17260         if(!this.internalRender){
17261             var main = this.el.dom.firstChild;
17262             var w = main.offsetWidth;
17263             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17264             Roo.fly(main).setWidth(w);
17265             this.internalRender = true;
17266             // opera does not respect the auto grow header center column
17267             // then, after it gets a width opera refuses to recalculate
17268             // without a second pass
17269             if(Roo.isOpera && !this.secondPass){
17270                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17271                 this.secondPass = true;
17272                 this.update.defer(10, this, [date]);
17273             }
17274         }
17275         */
17276         
17277     },
17278     
17279     findCell : function(dt) {
17280         dt = dt.clearTime().getTime();
17281         var ret = false;
17282         this.cells.each(function(c){
17283             //Roo.log("check " +c.dateValue + '?=' + dt);
17284             if(c.dateValue == dt){
17285                 ret = c;
17286                 return false;
17287             }
17288             return true;
17289         });
17290         
17291         return ret;
17292     },
17293     
17294     findCells : function(ev) {
17295         var s = ev.start.clone().clearTime().getTime();
17296        // Roo.log(s);
17297         var e= ev.end.clone().clearTime().getTime();
17298        // Roo.log(e);
17299         var ret = [];
17300         this.cells.each(function(c){
17301              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17302             
17303             if(c.dateValue > e){
17304                 return ;
17305             }
17306             if(c.dateValue < s){
17307                 return ;
17308             }
17309             ret.push(c);
17310         });
17311         
17312         return ret;    
17313     },
17314     
17315 //    findBestRow: function(cells)
17316 //    {
17317 //        var ret = 0;
17318 //        
17319 //        for (var i =0 ; i < cells.length;i++) {
17320 //            ret  = Math.max(cells[i].rows || 0,ret);
17321 //        }
17322 //        return ret;
17323 //        
17324 //    },
17325     
17326     
17327     addItem : function(ev)
17328     {
17329         // look for vertical location slot in
17330         var cells = this.findCells(ev);
17331         
17332 //        ev.row = this.findBestRow(cells);
17333         
17334         // work out the location.
17335         
17336         var crow = false;
17337         var rows = [];
17338         for(var i =0; i < cells.length; i++) {
17339             
17340             cells[i].row = cells[0].row;
17341             
17342             if(i == 0){
17343                 cells[i].row = cells[i].row + 1;
17344             }
17345             
17346             if (!crow) {
17347                 crow = {
17348                     start : cells[i],
17349                     end :  cells[i]
17350                 };
17351                 continue;
17352             }
17353             if (crow.start.getY() == cells[i].getY()) {
17354                 // on same row.
17355                 crow.end = cells[i];
17356                 continue;
17357             }
17358             // different row.
17359             rows.push(crow);
17360             crow = {
17361                 start: cells[i],
17362                 end : cells[i]
17363             };
17364             
17365         }
17366         
17367         rows.push(crow);
17368         ev.els = [];
17369         ev.rows = rows;
17370         ev.cells = cells;
17371         
17372         cells[0].events.push(ev);
17373         
17374         this.calevents.push(ev);
17375     },
17376     
17377     clearEvents: function() {
17378         
17379         if(!this.calevents){
17380             return;
17381         }
17382         
17383         Roo.each(this.cells.elements, function(c){
17384             c.row = 0;
17385             c.events = [];
17386             c.more = [];
17387         });
17388         
17389         Roo.each(this.calevents, function(e) {
17390             Roo.each(e.els, function(el) {
17391                 el.un('mouseenter' ,this.onEventEnter, this);
17392                 el.un('mouseleave' ,this.onEventLeave, this);
17393                 el.remove();
17394             },this);
17395         },this);
17396         
17397         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17398             e.remove();
17399         });
17400         
17401     },
17402     
17403     renderEvents: function()
17404     {   
17405         var _this = this;
17406         
17407         this.cells.each(function(c) {
17408             
17409             if(c.row < 5){
17410                 return;
17411             }
17412             
17413             var ev = c.events;
17414             
17415             var r = 4;
17416             if(c.row != c.events.length){
17417                 r = 4 - (4 - (c.row - c.events.length));
17418             }
17419             
17420             c.events = ev.slice(0, r);
17421             c.more = ev.slice(r);
17422             
17423             if(c.more.length && c.more.length == 1){
17424                 c.events.push(c.more.pop());
17425             }
17426             
17427             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17428             
17429         });
17430             
17431         this.cells.each(function(c) {
17432             
17433             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17434             
17435             
17436             for (var e = 0; e < c.events.length; e++){
17437                 var ev = c.events[e];
17438                 var rows = ev.rows;
17439                 
17440                 for(var i = 0; i < rows.length; i++) {
17441                 
17442                     // how many rows should it span..
17443
17444                     var  cfg = {
17445                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17446                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17447
17448                         unselectable : "on",
17449                         cn : [
17450                             {
17451                                 cls: 'fc-event-inner',
17452                                 cn : [
17453     //                                {
17454     //                                  tag:'span',
17455     //                                  cls: 'fc-event-time',
17456     //                                  html : cells.length > 1 ? '' : ev.time
17457     //                                },
17458                                     {
17459                                       tag:'span',
17460                                       cls: 'fc-event-title',
17461                                       html : String.format('{0}', ev.title)
17462                                     }
17463
17464
17465                                 ]
17466                             },
17467                             {
17468                                 cls: 'ui-resizable-handle ui-resizable-e',
17469                                 html : '&nbsp;&nbsp;&nbsp'
17470                             }
17471
17472                         ]
17473                     };
17474
17475                     if (i == 0) {
17476                         cfg.cls += ' fc-event-start';
17477                     }
17478                     if ((i+1) == rows.length) {
17479                         cfg.cls += ' fc-event-end';
17480                     }
17481
17482                     var ctr = _this.el.select('.fc-event-container',true).first();
17483                     var cg = ctr.createChild(cfg);
17484
17485                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17486                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17487
17488                     var r = (c.more.length) ? 1 : 0;
17489                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17490                     cg.setWidth(ebox.right - sbox.x -2);
17491
17492                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17493                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17494                     cg.on('click', _this.onEventClick, _this, ev);
17495
17496                     ev.els.push(cg);
17497                     
17498                 }
17499                 
17500             }
17501             
17502             
17503             if(c.more.length){
17504                 var  cfg = {
17505                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17506                     style : 'position: absolute',
17507                     unselectable : "on",
17508                     cn : [
17509                         {
17510                             cls: 'fc-event-inner',
17511                             cn : [
17512                                 {
17513                                   tag:'span',
17514                                   cls: 'fc-event-title',
17515                                   html : 'More'
17516                                 }
17517
17518
17519                             ]
17520                         },
17521                         {
17522                             cls: 'ui-resizable-handle ui-resizable-e',
17523                             html : '&nbsp;&nbsp;&nbsp'
17524                         }
17525
17526                     ]
17527                 };
17528
17529                 var ctr = _this.el.select('.fc-event-container',true).first();
17530                 var cg = ctr.createChild(cfg);
17531
17532                 var sbox = c.select('.fc-day-content',true).first().getBox();
17533                 var ebox = c.select('.fc-day-content',true).first().getBox();
17534                 //Roo.log(cg);
17535                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17536                 cg.setWidth(ebox.right - sbox.x -2);
17537
17538                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17539                 
17540             }
17541             
17542         });
17543         
17544         
17545         
17546     },
17547     
17548     onEventEnter: function (e, el,event,d) {
17549         this.fireEvent('evententer', this, el, event);
17550     },
17551     
17552     onEventLeave: function (e, el,event,d) {
17553         this.fireEvent('eventleave', this, el, event);
17554     },
17555     
17556     onEventClick: function (e, el,event,d) {
17557         this.fireEvent('eventclick', this, el, event);
17558     },
17559     
17560     onMonthChange: function () {
17561         this.store.load();
17562     },
17563     
17564     onMoreEventClick: function(e, el, more)
17565     {
17566         var _this = this;
17567         
17568         this.calpopover.placement = 'right';
17569         this.calpopover.setTitle('More');
17570         
17571         this.calpopover.setContent('');
17572         
17573         var ctr = this.calpopover.el.select('.popover-content', true).first();
17574         
17575         Roo.each(more, function(m){
17576             var cfg = {
17577                 cls : 'fc-event-hori fc-event-draggable',
17578                 html : m.title
17579             };
17580             var cg = ctr.createChild(cfg);
17581             
17582             cg.on('click', _this.onEventClick, _this, m);
17583         });
17584         
17585         this.calpopover.show(el);
17586         
17587         
17588     },
17589     
17590     onLoad: function () 
17591     {   
17592         this.calevents = [];
17593         var cal = this;
17594         
17595         if(this.store.getCount() > 0){
17596             this.store.data.each(function(d){
17597                cal.addItem({
17598                     id : d.data.id,
17599                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17600                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17601                     time : d.data.start_time,
17602                     title : d.data.title,
17603                     description : d.data.description,
17604                     venue : d.data.venue
17605                 });
17606             });
17607         }
17608         
17609         this.renderEvents();
17610         
17611         if(this.calevents.length && this.loadMask){
17612             this.maskEl.hide();
17613         }
17614     },
17615     
17616     onBeforeLoad: function()
17617     {
17618         this.clearEvents();
17619         if(this.loadMask){
17620             this.maskEl.show();
17621         }
17622     }
17623 });
17624
17625  
17626  /*
17627  * - LGPL
17628  *
17629  * element
17630  * 
17631  */
17632
17633 /**
17634  * @class Roo.bootstrap.Popover
17635  * @extends Roo.bootstrap.Component
17636  * Bootstrap Popover class
17637  * @cfg {String} html contents of the popover   (or false to use children..)
17638  * @cfg {String} title of popover (or false to hide)
17639  * @cfg {String} placement how it is placed
17640  * @cfg {String} trigger click || hover (or false to trigger manually)
17641  * @cfg {String} over what (parent or false to trigger manually.)
17642  * @cfg {Number} delay - delay before showing
17643  
17644  * @constructor
17645  * Create a new Popover
17646  * @param {Object} config The config object
17647  */
17648
17649 Roo.bootstrap.Popover = function(config){
17650     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17651     
17652     this.addEvents({
17653         // raw events
17654          /**
17655          * @event show
17656          * After the popover show
17657          * 
17658          * @param {Roo.bootstrap.Popover} this
17659          */
17660         "show" : true,
17661         /**
17662          * @event hide
17663          * After the popover hide
17664          * 
17665          * @param {Roo.bootstrap.Popover} this
17666          */
17667         "hide" : true
17668     });
17669 };
17670
17671 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17672     
17673     title: 'Fill in a title',
17674     html: false,
17675     
17676     placement : 'right',
17677     trigger : 'hover', // hover
17678     
17679     delay : 0,
17680     
17681     over: 'parent',
17682     
17683     can_build_overlaid : false,
17684     
17685     getChildContainer : function()
17686     {
17687         return this.el.select('.popover-content',true).first();
17688     },
17689     
17690     getAutoCreate : function(){
17691          
17692         var cfg = {
17693            cls : 'popover roo-dynamic',
17694            style: 'display:block',
17695            cn : [
17696                 {
17697                     cls : 'arrow'
17698                 },
17699                 {
17700                     cls : 'popover-inner',
17701                     cn : [
17702                         {
17703                             tag: 'h3',
17704                             cls: 'popover-title popover-header',
17705                             html : this.title
17706                         },
17707                         {
17708                             cls : 'popover-content popover-body',
17709                             html : this.html
17710                         }
17711                     ]
17712                     
17713                 }
17714            ]
17715         };
17716         
17717         return cfg;
17718     },
17719     setTitle: function(str)
17720     {
17721         this.title = str;
17722         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17723     },
17724     setContent: function(str)
17725     {
17726         this.html = str;
17727         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17728     },
17729     // as it get's added to the bottom of the page.
17730     onRender : function(ct, position)
17731     {
17732         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17733         if(!this.el){
17734             var cfg = Roo.apply({},  this.getAutoCreate());
17735             cfg.id = Roo.id();
17736             
17737             if (this.cls) {
17738                 cfg.cls += ' ' + this.cls;
17739             }
17740             if (this.style) {
17741                 cfg.style = this.style;
17742             }
17743             //Roo.log("adding to ");
17744             this.el = Roo.get(document.body).createChild(cfg, position);
17745 //            Roo.log(this.el);
17746         }
17747         this.initEvents();
17748     },
17749     
17750     initEvents : function()
17751     {
17752         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17753         this.el.enableDisplayMode('block');
17754         this.el.hide();
17755         if (this.over === false) {
17756             return; 
17757         }
17758         if (this.triggers === false) {
17759             return;
17760         }
17761         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17762         var triggers = this.trigger ? this.trigger.split(' ') : [];
17763         Roo.each(triggers, function(trigger) {
17764         
17765             if (trigger == 'click') {
17766                 on_el.on('click', this.toggle, this);
17767             } else if (trigger != 'manual') {
17768                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17769                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17770       
17771                 on_el.on(eventIn  ,this.enter, this);
17772                 on_el.on(eventOut, this.leave, this);
17773             }
17774         }, this);
17775         
17776     },
17777     
17778     
17779     // private
17780     timeout : null,
17781     hoverState : null,
17782     
17783     toggle : function () {
17784         this.hoverState == 'in' ? this.leave() : this.enter();
17785     },
17786     
17787     enter : function () {
17788         
17789         clearTimeout(this.timeout);
17790     
17791         this.hoverState = 'in';
17792     
17793         if (!this.delay || !this.delay.show) {
17794             this.show();
17795             return;
17796         }
17797         var _t = this;
17798         this.timeout = setTimeout(function () {
17799             if (_t.hoverState == 'in') {
17800                 _t.show();
17801             }
17802         }, this.delay.show)
17803     },
17804     
17805     leave : function() {
17806         clearTimeout(this.timeout);
17807     
17808         this.hoverState = 'out';
17809     
17810         if (!this.delay || !this.delay.hide) {
17811             this.hide();
17812             return;
17813         }
17814         var _t = this;
17815         this.timeout = setTimeout(function () {
17816             if (_t.hoverState == 'out') {
17817                 _t.hide();
17818             }
17819         }, this.delay.hide)
17820     },
17821     
17822     show : function (on_el)
17823     {
17824         if (!on_el) {
17825             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17826         }
17827         
17828         // set content.
17829         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17830         if (this.html !== false) {
17831             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17832         }
17833         this.el.removeClass([
17834             'fade','top','bottom', 'left', 'right','in',
17835             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17836         ]);
17837         if (!this.title.length) {
17838             this.el.select('.popover-title',true).hide();
17839         }
17840         
17841         var placement = typeof this.placement == 'function' ?
17842             this.placement.call(this, this.el, on_el) :
17843             this.placement;
17844             
17845         var autoToken = /\s?auto?\s?/i;
17846         var autoPlace = autoToken.test(placement);
17847         if (autoPlace) {
17848             placement = placement.replace(autoToken, '') || 'top';
17849         }
17850         
17851         //this.el.detach()
17852         //this.el.setXY([0,0]);
17853         this.el.show();
17854         this.el.dom.style.display='block';
17855         this.el.addClass(placement);
17856         
17857         //this.el.appendTo(on_el);
17858         
17859         var p = this.getPosition();
17860         var box = this.el.getBox();
17861         
17862         if (autoPlace) {
17863             // fixme..
17864         }
17865         var align = Roo.bootstrap.Popover.alignment[placement];
17866         
17867 //        Roo.log(align);
17868         this.el.alignTo(on_el, align[0],align[1]);
17869         //var arrow = this.el.select('.arrow',true).first();
17870         //arrow.set(align[2], 
17871         
17872         this.el.addClass('in');
17873         
17874         
17875         if (this.el.hasClass('fade')) {
17876             // fade it?
17877         }
17878         
17879         this.hoverState = 'in';
17880         
17881         this.fireEvent('show', this);
17882         
17883     },
17884     hide : function()
17885     {
17886         this.el.setXY([0,0]);
17887         this.el.removeClass('in');
17888         this.el.hide();
17889         this.hoverState = null;
17890         
17891         this.fireEvent('hide', this);
17892     }
17893     
17894 });
17895
17896 Roo.bootstrap.Popover.alignment = {
17897     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17898     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17899     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17900     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17901 };
17902
17903  /*
17904  * - LGPL
17905  *
17906  * Progress
17907  * 
17908  */
17909
17910 /**
17911  * @class Roo.bootstrap.Progress
17912  * @extends Roo.bootstrap.Component
17913  * Bootstrap Progress class
17914  * @cfg {Boolean} striped striped of the progress bar
17915  * @cfg {Boolean} active animated of the progress bar
17916  * 
17917  * 
17918  * @constructor
17919  * Create a new Progress
17920  * @param {Object} config The config object
17921  */
17922
17923 Roo.bootstrap.Progress = function(config){
17924     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17925 };
17926
17927 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17928     
17929     striped : false,
17930     active: false,
17931     
17932     getAutoCreate : function(){
17933         var cfg = {
17934             tag: 'div',
17935             cls: 'progress'
17936         };
17937         
17938         
17939         if(this.striped){
17940             cfg.cls += ' progress-striped';
17941         }
17942       
17943         if(this.active){
17944             cfg.cls += ' active';
17945         }
17946         
17947         
17948         return cfg;
17949     }
17950    
17951 });
17952
17953  
17954
17955  /*
17956  * - LGPL
17957  *
17958  * ProgressBar
17959  * 
17960  */
17961
17962 /**
17963  * @class Roo.bootstrap.ProgressBar
17964  * @extends Roo.bootstrap.Component
17965  * Bootstrap ProgressBar class
17966  * @cfg {Number} aria_valuenow aria-value now
17967  * @cfg {Number} aria_valuemin aria-value min
17968  * @cfg {Number} aria_valuemax aria-value max
17969  * @cfg {String} label label for the progress bar
17970  * @cfg {String} panel (success | info | warning | danger )
17971  * @cfg {String} role role of the progress bar
17972  * @cfg {String} sr_only text
17973  * 
17974  * 
17975  * @constructor
17976  * Create a new ProgressBar
17977  * @param {Object} config The config object
17978  */
17979
17980 Roo.bootstrap.ProgressBar = function(config){
17981     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17982 };
17983
17984 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17985     
17986     aria_valuenow : 0,
17987     aria_valuemin : 0,
17988     aria_valuemax : 100,
17989     label : false,
17990     panel : false,
17991     role : false,
17992     sr_only: false,
17993     
17994     getAutoCreate : function()
17995     {
17996         
17997         var cfg = {
17998             tag: 'div',
17999             cls: 'progress-bar',
18000             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18001         };
18002         
18003         if(this.sr_only){
18004             cfg.cn = {
18005                 tag: 'span',
18006                 cls: 'sr-only',
18007                 html: this.sr_only
18008             }
18009         }
18010         
18011         if(this.role){
18012             cfg.role = this.role;
18013         }
18014         
18015         if(this.aria_valuenow){
18016             cfg['aria-valuenow'] = this.aria_valuenow;
18017         }
18018         
18019         if(this.aria_valuemin){
18020             cfg['aria-valuemin'] = this.aria_valuemin;
18021         }
18022         
18023         if(this.aria_valuemax){
18024             cfg['aria-valuemax'] = this.aria_valuemax;
18025         }
18026         
18027         if(this.label && !this.sr_only){
18028             cfg.html = this.label;
18029         }
18030         
18031         if(this.panel){
18032             cfg.cls += ' progress-bar-' + this.panel;
18033         }
18034         
18035         return cfg;
18036     },
18037     
18038     update : function(aria_valuenow)
18039     {
18040         this.aria_valuenow = aria_valuenow;
18041         
18042         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18043     }
18044    
18045 });
18046
18047  
18048
18049  /*
18050  * - LGPL
18051  *
18052  * column
18053  * 
18054  */
18055
18056 /**
18057  * @class Roo.bootstrap.TabGroup
18058  * @extends Roo.bootstrap.Column
18059  * Bootstrap Column class
18060  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18061  * @cfg {Boolean} carousel true to make the group behave like a carousel
18062  * @cfg {Boolean} bullets show bullets for the panels
18063  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18064  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18065  * @cfg {Boolean} showarrow (true|false) show arrow default true
18066  * 
18067  * @constructor
18068  * Create a new TabGroup
18069  * @param {Object} config The config object
18070  */
18071
18072 Roo.bootstrap.TabGroup = function(config){
18073     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18074     if (!this.navId) {
18075         this.navId = Roo.id();
18076     }
18077     this.tabs = [];
18078     Roo.bootstrap.TabGroup.register(this);
18079     
18080 };
18081
18082 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18083     
18084     carousel : false,
18085     transition : false,
18086     bullets : 0,
18087     timer : 0,
18088     autoslide : false,
18089     slideFn : false,
18090     slideOnTouch : false,
18091     showarrow : true,
18092     
18093     getAutoCreate : function()
18094     {
18095         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18096         
18097         cfg.cls += ' tab-content';
18098         
18099         if (this.carousel) {
18100             cfg.cls += ' carousel slide';
18101             
18102             cfg.cn = [{
18103                cls : 'carousel-inner',
18104                cn : []
18105             }];
18106         
18107             if(this.bullets  && !Roo.isTouch){
18108                 
18109                 var bullets = {
18110                     cls : 'carousel-bullets',
18111                     cn : []
18112                 };
18113                
18114                 if(this.bullets_cls){
18115                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18116                 }
18117                 
18118                 bullets.cn.push({
18119                     cls : 'clear'
18120                 });
18121                 
18122                 cfg.cn[0].cn.push(bullets);
18123             }
18124             
18125             if(this.showarrow){
18126                 cfg.cn[0].cn.push({
18127                     tag : 'div',
18128                     class : 'carousel-arrow',
18129                     cn : [
18130                         {
18131                             tag : 'div',
18132                             class : 'carousel-prev',
18133                             cn : [
18134                                 {
18135                                     tag : 'i',
18136                                     class : 'fa fa-chevron-left'
18137                                 }
18138                             ]
18139                         },
18140                         {
18141                             tag : 'div',
18142                             class : 'carousel-next',
18143                             cn : [
18144                                 {
18145                                     tag : 'i',
18146                                     class : 'fa fa-chevron-right'
18147                                 }
18148                             ]
18149                         }
18150                     ]
18151                 });
18152             }
18153             
18154         }
18155         
18156         return cfg;
18157     },
18158     
18159     initEvents:  function()
18160     {
18161 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18162 //            this.el.on("touchstart", this.onTouchStart, this);
18163 //        }
18164         
18165         if(this.autoslide){
18166             var _this = this;
18167             
18168             this.slideFn = window.setInterval(function() {
18169                 _this.showPanelNext();
18170             }, this.timer);
18171         }
18172         
18173         if(this.showarrow){
18174             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18175             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18176         }
18177         
18178         
18179     },
18180     
18181 //    onTouchStart : function(e, el, o)
18182 //    {
18183 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18184 //            return;
18185 //        }
18186 //        
18187 //        this.showPanelNext();
18188 //    },
18189     
18190     
18191     getChildContainer : function()
18192     {
18193         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18194     },
18195     
18196     /**
18197     * register a Navigation item
18198     * @param {Roo.bootstrap.NavItem} the navitem to add
18199     */
18200     register : function(item)
18201     {
18202         this.tabs.push( item);
18203         item.navId = this.navId; // not really needed..
18204         this.addBullet();
18205     
18206     },
18207     
18208     getActivePanel : function()
18209     {
18210         var r = false;
18211         Roo.each(this.tabs, function(t) {
18212             if (t.active) {
18213                 r = t;
18214                 return false;
18215             }
18216             return null;
18217         });
18218         return r;
18219         
18220     },
18221     getPanelByName : function(n)
18222     {
18223         var r = false;
18224         Roo.each(this.tabs, function(t) {
18225             if (t.tabId == n) {
18226                 r = t;
18227                 return false;
18228             }
18229             return null;
18230         });
18231         return r;
18232     },
18233     indexOfPanel : function(p)
18234     {
18235         var r = false;
18236         Roo.each(this.tabs, function(t,i) {
18237             if (t.tabId == p.tabId) {
18238                 r = i;
18239                 return false;
18240             }
18241             return null;
18242         });
18243         return r;
18244     },
18245     /**
18246      * show a specific panel
18247      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18248      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18249      */
18250     showPanel : function (pan)
18251     {
18252         if(this.transition || typeof(pan) == 'undefined'){
18253             Roo.log("waiting for the transitionend");
18254             return;
18255         }
18256         
18257         if (typeof(pan) == 'number') {
18258             pan = this.tabs[pan];
18259         }
18260         
18261         if (typeof(pan) == 'string') {
18262             pan = this.getPanelByName(pan);
18263         }
18264         
18265         var cur = this.getActivePanel();
18266         
18267         if(!pan || !cur){
18268             Roo.log('pan or acitve pan is undefined');
18269             return false;
18270         }
18271         
18272         if (pan.tabId == this.getActivePanel().tabId) {
18273             return true;
18274         }
18275         
18276         if (false === cur.fireEvent('beforedeactivate')) {
18277             return false;
18278         }
18279         
18280         if(this.bullets > 0 && !Roo.isTouch){
18281             this.setActiveBullet(this.indexOfPanel(pan));
18282         }
18283         
18284         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18285             
18286             this.transition = true;
18287             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18288             var lr = dir == 'next' ? 'left' : 'right';
18289             pan.el.addClass(dir); // or prev
18290             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18291             cur.el.addClass(lr); // or right
18292             pan.el.addClass(lr);
18293             
18294             var _this = this;
18295             cur.el.on('transitionend', function() {
18296                 Roo.log("trans end?");
18297                 
18298                 pan.el.removeClass([lr,dir]);
18299                 pan.setActive(true);
18300                 
18301                 cur.el.removeClass([lr]);
18302                 cur.setActive(false);
18303                 
18304                 _this.transition = false;
18305                 
18306             }, this, { single:  true } );
18307             
18308             return true;
18309         }
18310         
18311         cur.setActive(false);
18312         pan.setActive(true);
18313         
18314         return true;
18315         
18316     },
18317     showPanelNext : function()
18318     {
18319         var i = this.indexOfPanel(this.getActivePanel());
18320         
18321         if (i >= this.tabs.length - 1 && !this.autoslide) {
18322             return;
18323         }
18324         
18325         if (i >= this.tabs.length - 1 && this.autoslide) {
18326             i = -1;
18327         }
18328         
18329         this.showPanel(this.tabs[i+1]);
18330     },
18331     
18332     showPanelPrev : function()
18333     {
18334         var i = this.indexOfPanel(this.getActivePanel());
18335         
18336         if (i  < 1 && !this.autoslide) {
18337             return;
18338         }
18339         
18340         if (i < 1 && this.autoslide) {
18341             i = this.tabs.length;
18342         }
18343         
18344         this.showPanel(this.tabs[i-1]);
18345     },
18346     
18347     
18348     addBullet: function()
18349     {
18350         if(!this.bullets || Roo.isTouch){
18351             return;
18352         }
18353         var ctr = this.el.select('.carousel-bullets',true).first();
18354         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18355         var bullet = ctr.createChild({
18356             cls : 'bullet bullet-' + i
18357         },ctr.dom.lastChild);
18358         
18359         
18360         var _this = this;
18361         
18362         bullet.on('click', (function(e, el, o, ii, t){
18363
18364             e.preventDefault();
18365
18366             this.showPanel(ii);
18367
18368             if(this.autoslide && this.slideFn){
18369                 clearInterval(this.slideFn);
18370                 this.slideFn = window.setInterval(function() {
18371                     _this.showPanelNext();
18372                 }, this.timer);
18373             }
18374
18375         }).createDelegate(this, [i, bullet], true));
18376                 
18377         
18378     },
18379      
18380     setActiveBullet : function(i)
18381     {
18382         if(Roo.isTouch){
18383             return;
18384         }
18385         
18386         Roo.each(this.el.select('.bullet', true).elements, function(el){
18387             el.removeClass('selected');
18388         });
18389
18390         var bullet = this.el.select('.bullet-' + i, true).first();
18391         
18392         if(!bullet){
18393             return;
18394         }
18395         
18396         bullet.addClass('selected');
18397     }
18398     
18399     
18400   
18401 });
18402
18403  
18404
18405  
18406  
18407 Roo.apply(Roo.bootstrap.TabGroup, {
18408     
18409     groups: {},
18410      /**
18411     * register a Navigation Group
18412     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18413     */
18414     register : function(navgrp)
18415     {
18416         this.groups[navgrp.navId] = navgrp;
18417         
18418     },
18419     /**
18420     * fetch a Navigation Group based on the navigation ID
18421     * if one does not exist , it will get created.
18422     * @param {string} the navgroup to add
18423     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18424     */
18425     get: function(navId) {
18426         if (typeof(this.groups[navId]) == 'undefined') {
18427             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18428         }
18429         return this.groups[navId] ;
18430     }
18431     
18432     
18433     
18434 });
18435
18436  /*
18437  * - LGPL
18438  *
18439  * TabPanel
18440  * 
18441  */
18442
18443 /**
18444  * @class Roo.bootstrap.TabPanel
18445  * @extends Roo.bootstrap.Component
18446  * Bootstrap TabPanel class
18447  * @cfg {Boolean} active panel active
18448  * @cfg {String} html panel content
18449  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18450  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18451  * @cfg {String} href click to link..
18452  * 
18453  * 
18454  * @constructor
18455  * Create a new TabPanel
18456  * @param {Object} config The config object
18457  */
18458
18459 Roo.bootstrap.TabPanel = function(config){
18460     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18461     this.addEvents({
18462         /**
18463              * @event changed
18464              * Fires when the active status changes
18465              * @param {Roo.bootstrap.TabPanel} this
18466              * @param {Boolean} state the new state
18467             
18468          */
18469         'changed': true,
18470         /**
18471              * @event beforedeactivate
18472              * Fires before a tab is de-activated - can be used to do validation on a form.
18473              * @param {Roo.bootstrap.TabPanel} this
18474              * @return {Boolean} false if there is an error
18475             
18476          */
18477         'beforedeactivate': true
18478      });
18479     
18480     this.tabId = this.tabId || Roo.id();
18481   
18482 };
18483
18484 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18485     
18486     active: false,
18487     html: false,
18488     tabId: false,
18489     navId : false,
18490     href : '',
18491     
18492     getAutoCreate : function(){
18493         var cfg = {
18494             tag: 'div',
18495             // item is needed for carousel - not sure if it has any effect otherwise
18496             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18497             html: this.html || ''
18498         };
18499         
18500         if(this.active){
18501             cfg.cls += ' active';
18502         }
18503         
18504         if(this.tabId){
18505             cfg.tabId = this.tabId;
18506         }
18507         
18508         
18509         return cfg;
18510     },
18511     
18512     initEvents:  function()
18513     {
18514         var p = this.parent();
18515         
18516         this.navId = this.navId || p.navId;
18517         
18518         if (typeof(this.navId) != 'undefined') {
18519             // not really needed.. but just in case.. parent should be a NavGroup.
18520             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18521             
18522             tg.register(this);
18523             
18524             var i = tg.tabs.length - 1;
18525             
18526             if(this.active && tg.bullets > 0 && i < tg.bullets){
18527                 tg.setActiveBullet(i);
18528             }
18529         }
18530         
18531         this.el.on('click', this.onClick, this);
18532         
18533         if(Roo.isTouch){
18534             this.el.on("touchstart", this.onTouchStart, this);
18535             this.el.on("touchmove", this.onTouchMove, this);
18536             this.el.on("touchend", this.onTouchEnd, this);
18537         }
18538         
18539     },
18540     
18541     onRender : function(ct, position)
18542     {
18543         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18544     },
18545     
18546     setActive : function(state)
18547     {
18548         Roo.log("panel - set active " + this.tabId + "=" + state);
18549         
18550         this.active = state;
18551         if (!state) {
18552             this.el.removeClass('active');
18553             
18554         } else  if (!this.el.hasClass('active')) {
18555             this.el.addClass('active');
18556         }
18557         
18558         this.fireEvent('changed', this, state);
18559     },
18560     
18561     onClick : function(e)
18562     {
18563         e.preventDefault();
18564         
18565         if(!this.href.length){
18566             return;
18567         }
18568         
18569         window.location.href = this.href;
18570     },
18571     
18572     startX : 0,
18573     startY : 0,
18574     endX : 0,
18575     endY : 0,
18576     swiping : false,
18577     
18578     onTouchStart : function(e)
18579     {
18580         this.swiping = false;
18581         
18582         this.startX = e.browserEvent.touches[0].clientX;
18583         this.startY = e.browserEvent.touches[0].clientY;
18584     },
18585     
18586     onTouchMove : function(e)
18587     {
18588         this.swiping = true;
18589         
18590         this.endX = e.browserEvent.touches[0].clientX;
18591         this.endY = e.browserEvent.touches[0].clientY;
18592     },
18593     
18594     onTouchEnd : function(e)
18595     {
18596         if(!this.swiping){
18597             this.onClick(e);
18598             return;
18599         }
18600         
18601         var tabGroup = this.parent();
18602         
18603         if(this.endX > this.startX){ // swiping right
18604             tabGroup.showPanelPrev();
18605             return;
18606         }
18607         
18608         if(this.startX > this.endX){ // swiping left
18609             tabGroup.showPanelNext();
18610             return;
18611         }
18612     }
18613     
18614     
18615 });
18616  
18617
18618  
18619
18620  /*
18621  * - LGPL
18622  *
18623  * DateField
18624  * 
18625  */
18626
18627 /**
18628  * @class Roo.bootstrap.DateField
18629  * @extends Roo.bootstrap.Input
18630  * Bootstrap DateField class
18631  * @cfg {Number} weekStart default 0
18632  * @cfg {String} viewMode default empty, (months|years)
18633  * @cfg {String} minViewMode default empty, (months|years)
18634  * @cfg {Number} startDate default -Infinity
18635  * @cfg {Number} endDate default Infinity
18636  * @cfg {Boolean} todayHighlight default false
18637  * @cfg {Boolean} todayBtn default false
18638  * @cfg {Boolean} calendarWeeks default false
18639  * @cfg {Object} daysOfWeekDisabled default empty
18640  * @cfg {Boolean} singleMode default false (true | false)
18641  * 
18642  * @cfg {Boolean} keyboardNavigation default true
18643  * @cfg {String} language default en
18644  * 
18645  * @constructor
18646  * Create a new DateField
18647  * @param {Object} config The config object
18648  */
18649
18650 Roo.bootstrap.DateField = function(config){
18651     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18652      this.addEvents({
18653             /**
18654              * @event show
18655              * Fires when this field show.
18656              * @param {Roo.bootstrap.DateField} this
18657              * @param {Mixed} date The date value
18658              */
18659             show : true,
18660             /**
18661              * @event show
18662              * Fires when this field hide.
18663              * @param {Roo.bootstrap.DateField} this
18664              * @param {Mixed} date The date value
18665              */
18666             hide : true,
18667             /**
18668              * @event select
18669              * Fires when select a date.
18670              * @param {Roo.bootstrap.DateField} this
18671              * @param {Mixed} date The date value
18672              */
18673             select : true,
18674             /**
18675              * @event beforeselect
18676              * Fires when before select a date.
18677              * @param {Roo.bootstrap.DateField} this
18678              * @param {Mixed} date The date value
18679              */
18680             beforeselect : true
18681         });
18682 };
18683
18684 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18685     
18686     /**
18687      * @cfg {String} format
18688      * The default date format string which can be overriden for localization support.  The format must be
18689      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18690      */
18691     format : "m/d/y",
18692     /**
18693      * @cfg {String} altFormats
18694      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18695      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18696      */
18697     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18698     
18699     weekStart : 0,
18700     
18701     viewMode : '',
18702     
18703     minViewMode : '',
18704     
18705     todayHighlight : false,
18706     
18707     todayBtn: false,
18708     
18709     language: 'en',
18710     
18711     keyboardNavigation: true,
18712     
18713     calendarWeeks: false,
18714     
18715     startDate: -Infinity,
18716     
18717     endDate: Infinity,
18718     
18719     daysOfWeekDisabled: [],
18720     
18721     _events: [],
18722     
18723     singleMode : false,
18724     
18725     UTCDate: function()
18726     {
18727         return new Date(Date.UTC.apply(Date, arguments));
18728     },
18729     
18730     UTCToday: function()
18731     {
18732         var today = new Date();
18733         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18734     },
18735     
18736     getDate: function() {
18737             var d = this.getUTCDate();
18738             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18739     },
18740     
18741     getUTCDate: function() {
18742             return this.date;
18743     },
18744     
18745     setDate: function(d) {
18746             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18747     },
18748     
18749     setUTCDate: function(d) {
18750             this.date = d;
18751             this.setValue(this.formatDate(this.date));
18752     },
18753         
18754     onRender: function(ct, position)
18755     {
18756         
18757         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18758         
18759         this.language = this.language || 'en';
18760         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18761         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18762         
18763         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18764         this.format = this.format || 'm/d/y';
18765         this.isInline = false;
18766         this.isInput = true;
18767         this.component = this.el.select('.add-on', true).first() || false;
18768         this.component = (this.component && this.component.length === 0) ? false : this.component;
18769         this.hasInput = this.component && this.inputEl().length;
18770         
18771         if (typeof(this.minViewMode === 'string')) {
18772             switch (this.minViewMode) {
18773                 case 'months':
18774                     this.minViewMode = 1;
18775                     break;
18776                 case 'years':
18777                     this.minViewMode = 2;
18778                     break;
18779                 default:
18780                     this.minViewMode = 0;
18781                     break;
18782             }
18783         }
18784         
18785         if (typeof(this.viewMode === 'string')) {
18786             switch (this.viewMode) {
18787                 case 'months':
18788                     this.viewMode = 1;
18789                     break;
18790                 case 'years':
18791                     this.viewMode = 2;
18792                     break;
18793                 default:
18794                     this.viewMode = 0;
18795                     break;
18796             }
18797         }
18798                 
18799         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18800         
18801 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18802         
18803         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18804         
18805         this.picker().on('mousedown', this.onMousedown, this);
18806         this.picker().on('click', this.onClick, this);
18807         
18808         this.picker().addClass('datepicker-dropdown');
18809         
18810         this.startViewMode = this.viewMode;
18811         
18812         if(this.singleMode){
18813             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18814                 v.setVisibilityMode(Roo.Element.DISPLAY);
18815                 v.hide();
18816             });
18817             
18818             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18819                 v.setStyle('width', '189px');
18820             });
18821         }
18822         
18823         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18824             if(!this.calendarWeeks){
18825                 v.remove();
18826                 return;
18827             }
18828             
18829             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18830             v.attr('colspan', function(i, val){
18831                 return parseInt(val) + 1;
18832             });
18833         });
18834                         
18835         
18836         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18837         
18838         this.setStartDate(this.startDate);
18839         this.setEndDate(this.endDate);
18840         
18841         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18842         
18843         this.fillDow();
18844         this.fillMonths();
18845         this.update();
18846         this.showMode();
18847         
18848         if(this.isInline) {
18849             this.showPopup();
18850         }
18851     },
18852     
18853     picker : function()
18854     {
18855         return this.pickerEl;
18856 //        return this.el.select('.datepicker', true).first();
18857     },
18858     
18859     fillDow: function()
18860     {
18861         var dowCnt = this.weekStart;
18862         
18863         var dow = {
18864             tag: 'tr',
18865             cn: [
18866                 
18867             ]
18868         };
18869         
18870         if(this.calendarWeeks){
18871             dow.cn.push({
18872                 tag: 'th',
18873                 cls: 'cw',
18874                 html: '&nbsp;'
18875             })
18876         }
18877         
18878         while (dowCnt < this.weekStart + 7) {
18879             dow.cn.push({
18880                 tag: 'th',
18881                 cls: 'dow',
18882                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18883             });
18884         }
18885         
18886         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18887     },
18888     
18889     fillMonths: function()
18890     {    
18891         var i = 0;
18892         var months = this.picker().select('>.datepicker-months td', true).first();
18893         
18894         months.dom.innerHTML = '';
18895         
18896         while (i < 12) {
18897             var month = {
18898                 tag: 'span',
18899                 cls: 'month',
18900                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18901             };
18902             
18903             months.createChild(month);
18904         }
18905         
18906     },
18907     
18908     update: function()
18909     {
18910         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;
18911         
18912         if (this.date < this.startDate) {
18913             this.viewDate = new Date(this.startDate);
18914         } else if (this.date > this.endDate) {
18915             this.viewDate = new Date(this.endDate);
18916         } else {
18917             this.viewDate = new Date(this.date);
18918         }
18919         
18920         this.fill();
18921     },
18922     
18923     fill: function() 
18924     {
18925         var d = new Date(this.viewDate),
18926                 year = d.getUTCFullYear(),
18927                 month = d.getUTCMonth(),
18928                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18929                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18930                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18931                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18932                 currentDate = this.date && this.date.valueOf(),
18933                 today = this.UTCToday();
18934         
18935         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18936         
18937 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18938         
18939 //        this.picker.select('>tfoot th.today').
18940 //                                              .text(dates[this.language].today)
18941 //                                              .toggle(this.todayBtn !== false);
18942     
18943         this.updateNavArrows();
18944         this.fillMonths();
18945                                                 
18946         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18947         
18948         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18949          
18950         prevMonth.setUTCDate(day);
18951         
18952         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18953         
18954         var nextMonth = new Date(prevMonth);
18955         
18956         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18957         
18958         nextMonth = nextMonth.valueOf();
18959         
18960         var fillMonths = false;
18961         
18962         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18963         
18964         while(prevMonth.valueOf() <= nextMonth) {
18965             var clsName = '';
18966             
18967             if (prevMonth.getUTCDay() === this.weekStart) {
18968                 if(fillMonths){
18969                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18970                 }
18971                     
18972                 fillMonths = {
18973                     tag: 'tr',
18974                     cn: []
18975                 };
18976                 
18977                 if(this.calendarWeeks){
18978                     // ISO 8601: First week contains first thursday.
18979                     // ISO also states week starts on Monday, but we can be more abstract here.
18980                     var
18981                     // Start of current week: based on weekstart/current date
18982                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18983                     // Thursday of this week
18984                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18985                     // First Thursday of year, year from thursday
18986                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18987                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18988                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18989                     
18990                     fillMonths.cn.push({
18991                         tag: 'td',
18992                         cls: 'cw',
18993                         html: calWeek
18994                     });
18995                 }
18996             }
18997             
18998             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18999                 clsName += ' old';
19000             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19001                 clsName += ' new';
19002             }
19003             if (this.todayHighlight &&
19004                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19005                 prevMonth.getUTCMonth() == today.getMonth() &&
19006                 prevMonth.getUTCDate() == today.getDate()) {
19007                 clsName += ' today';
19008             }
19009             
19010             if (currentDate && prevMonth.valueOf() === currentDate) {
19011                 clsName += ' active';
19012             }
19013             
19014             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19015                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19016                     clsName += ' disabled';
19017             }
19018             
19019             fillMonths.cn.push({
19020                 tag: 'td',
19021                 cls: 'day ' + clsName,
19022                 html: prevMonth.getDate()
19023             });
19024             
19025             prevMonth.setDate(prevMonth.getDate()+1);
19026         }
19027           
19028         var currentYear = this.date && this.date.getUTCFullYear();
19029         var currentMonth = this.date && this.date.getUTCMonth();
19030         
19031         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19032         
19033         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19034             v.removeClass('active');
19035             
19036             if(currentYear === year && k === currentMonth){
19037                 v.addClass('active');
19038             }
19039             
19040             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19041                 v.addClass('disabled');
19042             }
19043             
19044         });
19045         
19046         
19047         year = parseInt(year/10, 10) * 10;
19048         
19049         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19050         
19051         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19052         
19053         year -= 1;
19054         for (var i = -1; i < 11; i++) {
19055             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19056                 tag: 'span',
19057                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19058                 html: year
19059             });
19060             
19061             year += 1;
19062         }
19063     },
19064     
19065     showMode: function(dir) 
19066     {
19067         if (dir) {
19068             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19069         }
19070         
19071         Roo.each(this.picker().select('>div',true).elements, function(v){
19072             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19073             v.hide();
19074         });
19075         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19076     },
19077     
19078     place: function()
19079     {
19080         if(this.isInline) {
19081             return;
19082         }
19083         
19084         this.picker().removeClass(['bottom', 'top']);
19085         
19086         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19087             /*
19088              * place to the top of element!
19089              *
19090              */
19091             
19092             this.picker().addClass('top');
19093             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19094             
19095             return;
19096         }
19097         
19098         this.picker().addClass('bottom');
19099         
19100         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19101     },
19102     
19103     parseDate : function(value)
19104     {
19105         if(!value || value instanceof Date){
19106             return value;
19107         }
19108         var v = Date.parseDate(value, this.format);
19109         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19110             v = Date.parseDate(value, 'Y-m-d');
19111         }
19112         if(!v && this.altFormats){
19113             if(!this.altFormatsArray){
19114                 this.altFormatsArray = this.altFormats.split("|");
19115             }
19116             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19117                 v = Date.parseDate(value, this.altFormatsArray[i]);
19118             }
19119         }
19120         return v;
19121     },
19122     
19123     formatDate : function(date, fmt)
19124     {   
19125         return (!date || !(date instanceof Date)) ?
19126         date : date.dateFormat(fmt || this.format);
19127     },
19128     
19129     onFocus : function()
19130     {
19131         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19132         this.showPopup();
19133     },
19134     
19135     onBlur : function()
19136     {
19137         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19138         
19139         var d = this.inputEl().getValue();
19140         
19141         this.setValue(d);
19142                 
19143         this.hidePopup();
19144     },
19145     
19146     showPopup : function()
19147     {
19148         this.picker().show();
19149         this.update();
19150         this.place();
19151         
19152         this.fireEvent('showpopup', this, this.date);
19153     },
19154     
19155     hidePopup : function()
19156     {
19157         if(this.isInline) {
19158             return;
19159         }
19160         this.picker().hide();
19161         this.viewMode = this.startViewMode;
19162         this.showMode();
19163         
19164         this.fireEvent('hidepopup', this, this.date);
19165         
19166     },
19167     
19168     onMousedown: function(e)
19169     {
19170         e.stopPropagation();
19171         e.preventDefault();
19172     },
19173     
19174     keyup: function(e)
19175     {
19176         Roo.bootstrap.DateField.superclass.keyup.call(this);
19177         this.update();
19178     },
19179
19180     setValue: function(v)
19181     {
19182         if(this.fireEvent('beforeselect', this, v) !== false){
19183             var d = new Date(this.parseDate(v) ).clearTime();
19184         
19185             if(isNaN(d.getTime())){
19186                 this.date = this.viewDate = '';
19187                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19188                 return;
19189             }
19190
19191             v = this.formatDate(d);
19192
19193             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19194
19195             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19196
19197             this.update();
19198
19199             this.fireEvent('select', this, this.date);
19200         }
19201     },
19202     
19203     getValue: function()
19204     {
19205         return this.formatDate(this.date);
19206     },
19207     
19208     fireKey: function(e)
19209     {
19210         if (!this.picker().isVisible()){
19211             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19212                 this.showPopup();
19213             }
19214             return;
19215         }
19216         
19217         var dateChanged = false,
19218         dir, day, month,
19219         newDate, newViewDate;
19220         
19221         switch(e.keyCode){
19222             case 27: // escape
19223                 this.hidePopup();
19224                 e.preventDefault();
19225                 break;
19226             case 37: // left
19227             case 39: // right
19228                 if (!this.keyboardNavigation) {
19229                     break;
19230                 }
19231                 dir = e.keyCode == 37 ? -1 : 1;
19232                 
19233                 if (e.ctrlKey){
19234                     newDate = this.moveYear(this.date, dir);
19235                     newViewDate = this.moveYear(this.viewDate, dir);
19236                 } else if (e.shiftKey){
19237                     newDate = this.moveMonth(this.date, dir);
19238                     newViewDate = this.moveMonth(this.viewDate, dir);
19239                 } else {
19240                     newDate = new Date(this.date);
19241                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19242                     newViewDate = new Date(this.viewDate);
19243                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19244                 }
19245                 if (this.dateWithinRange(newDate)){
19246                     this.date = newDate;
19247                     this.viewDate = newViewDate;
19248                     this.setValue(this.formatDate(this.date));
19249 //                    this.update();
19250                     e.preventDefault();
19251                     dateChanged = true;
19252                 }
19253                 break;
19254             case 38: // up
19255             case 40: // down
19256                 if (!this.keyboardNavigation) {
19257                     break;
19258                 }
19259                 dir = e.keyCode == 38 ? -1 : 1;
19260                 if (e.ctrlKey){
19261                     newDate = this.moveYear(this.date, dir);
19262                     newViewDate = this.moveYear(this.viewDate, dir);
19263                 } else if (e.shiftKey){
19264                     newDate = this.moveMonth(this.date, dir);
19265                     newViewDate = this.moveMonth(this.viewDate, dir);
19266                 } else {
19267                     newDate = new Date(this.date);
19268                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19269                     newViewDate = new Date(this.viewDate);
19270                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19271                 }
19272                 if (this.dateWithinRange(newDate)){
19273                     this.date = newDate;
19274                     this.viewDate = newViewDate;
19275                     this.setValue(this.formatDate(this.date));
19276 //                    this.update();
19277                     e.preventDefault();
19278                     dateChanged = true;
19279                 }
19280                 break;
19281             case 13: // enter
19282                 this.setValue(this.formatDate(this.date));
19283                 this.hidePopup();
19284                 e.preventDefault();
19285                 break;
19286             case 9: // tab
19287                 this.setValue(this.formatDate(this.date));
19288                 this.hidePopup();
19289                 break;
19290             case 16: // shift
19291             case 17: // ctrl
19292             case 18: // alt
19293                 break;
19294             default :
19295                 this.hidePopup();
19296                 
19297         }
19298     },
19299     
19300     
19301     onClick: function(e) 
19302     {
19303         e.stopPropagation();
19304         e.preventDefault();
19305         
19306         var target = e.getTarget();
19307         
19308         if(target.nodeName.toLowerCase() === 'i'){
19309             target = Roo.get(target).dom.parentNode;
19310         }
19311         
19312         var nodeName = target.nodeName;
19313         var className = target.className;
19314         var html = target.innerHTML;
19315         //Roo.log(nodeName);
19316         
19317         switch(nodeName.toLowerCase()) {
19318             case 'th':
19319                 switch(className) {
19320                     case 'switch':
19321                         this.showMode(1);
19322                         break;
19323                     case 'prev':
19324                     case 'next':
19325                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19326                         switch(this.viewMode){
19327                                 case 0:
19328                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19329                                         break;
19330                                 case 1:
19331                                 case 2:
19332                                         this.viewDate = this.moveYear(this.viewDate, dir);
19333                                         break;
19334                         }
19335                         this.fill();
19336                         break;
19337                     case 'today':
19338                         var date = new Date();
19339                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19340 //                        this.fill()
19341                         this.setValue(this.formatDate(this.date));
19342                         
19343                         this.hidePopup();
19344                         break;
19345                 }
19346                 break;
19347             case 'span':
19348                 if (className.indexOf('disabled') < 0) {
19349                     this.viewDate.setUTCDate(1);
19350                     if (className.indexOf('month') > -1) {
19351                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19352                     } else {
19353                         var year = parseInt(html, 10) || 0;
19354                         this.viewDate.setUTCFullYear(year);
19355                         
19356                     }
19357                     
19358                     if(this.singleMode){
19359                         this.setValue(this.formatDate(this.viewDate));
19360                         this.hidePopup();
19361                         return;
19362                     }
19363                     
19364                     this.showMode(-1);
19365                     this.fill();
19366                 }
19367                 break;
19368                 
19369             case 'td':
19370                 //Roo.log(className);
19371                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19372                     var day = parseInt(html, 10) || 1;
19373                     var year = this.viewDate.getUTCFullYear(),
19374                         month = this.viewDate.getUTCMonth();
19375
19376                     if (className.indexOf('old') > -1) {
19377                         if(month === 0 ){
19378                             month = 11;
19379                             year -= 1;
19380                         }else{
19381                             month -= 1;
19382                         }
19383                     } else if (className.indexOf('new') > -1) {
19384                         if (month == 11) {
19385                             month = 0;
19386                             year += 1;
19387                         } else {
19388                             month += 1;
19389                         }
19390                     }
19391                     //Roo.log([year,month,day]);
19392                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19393                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19394 //                    this.fill();
19395                     //Roo.log(this.formatDate(this.date));
19396                     this.setValue(this.formatDate(this.date));
19397                     this.hidePopup();
19398                 }
19399                 break;
19400         }
19401     },
19402     
19403     setStartDate: function(startDate)
19404     {
19405         this.startDate = startDate || -Infinity;
19406         if (this.startDate !== -Infinity) {
19407             this.startDate = this.parseDate(this.startDate);
19408         }
19409         this.update();
19410         this.updateNavArrows();
19411     },
19412
19413     setEndDate: function(endDate)
19414     {
19415         this.endDate = endDate || Infinity;
19416         if (this.endDate !== Infinity) {
19417             this.endDate = this.parseDate(this.endDate);
19418         }
19419         this.update();
19420         this.updateNavArrows();
19421     },
19422     
19423     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19424     {
19425         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19426         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19427             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19428         }
19429         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19430             return parseInt(d, 10);
19431         });
19432         this.update();
19433         this.updateNavArrows();
19434     },
19435     
19436     updateNavArrows: function() 
19437     {
19438         if(this.singleMode){
19439             return;
19440         }
19441         
19442         var d = new Date(this.viewDate),
19443         year = d.getUTCFullYear(),
19444         month = d.getUTCMonth();
19445         
19446         Roo.each(this.picker().select('.prev', true).elements, function(v){
19447             v.show();
19448             switch (this.viewMode) {
19449                 case 0:
19450
19451                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19452                         v.hide();
19453                     }
19454                     break;
19455                 case 1:
19456                 case 2:
19457                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19458                         v.hide();
19459                     }
19460                     break;
19461             }
19462         });
19463         
19464         Roo.each(this.picker().select('.next', true).elements, function(v){
19465             v.show();
19466             switch (this.viewMode) {
19467                 case 0:
19468
19469                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19470                         v.hide();
19471                     }
19472                     break;
19473                 case 1:
19474                 case 2:
19475                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19476                         v.hide();
19477                     }
19478                     break;
19479             }
19480         })
19481     },
19482     
19483     moveMonth: function(date, dir)
19484     {
19485         if (!dir) {
19486             return date;
19487         }
19488         var new_date = new Date(date.valueOf()),
19489         day = new_date.getUTCDate(),
19490         month = new_date.getUTCMonth(),
19491         mag = Math.abs(dir),
19492         new_month, test;
19493         dir = dir > 0 ? 1 : -1;
19494         if (mag == 1){
19495             test = dir == -1
19496             // If going back one month, make sure month is not current month
19497             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19498             ? function(){
19499                 return new_date.getUTCMonth() == month;
19500             }
19501             // If going forward one month, make sure month is as expected
19502             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19503             : function(){
19504                 return new_date.getUTCMonth() != new_month;
19505             };
19506             new_month = month + dir;
19507             new_date.setUTCMonth(new_month);
19508             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19509             if (new_month < 0 || new_month > 11) {
19510                 new_month = (new_month + 12) % 12;
19511             }
19512         } else {
19513             // For magnitudes >1, move one month at a time...
19514             for (var i=0; i<mag; i++) {
19515                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19516                 new_date = this.moveMonth(new_date, dir);
19517             }
19518             // ...then reset the day, keeping it in the new month
19519             new_month = new_date.getUTCMonth();
19520             new_date.setUTCDate(day);
19521             test = function(){
19522                 return new_month != new_date.getUTCMonth();
19523             };
19524         }
19525         // Common date-resetting loop -- if date is beyond end of month, make it
19526         // end of month
19527         while (test()){
19528             new_date.setUTCDate(--day);
19529             new_date.setUTCMonth(new_month);
19530         }
19531         return new_date;
19532     },
19533
19534     moveYear: function(date, dir)
19535     {
19536         return this.moveMonth(date, dir*12);
19537     },
19538
19539     dateWithinRange: function(date)
19540     {
19541         return date >= this.startDate && date <= this.endDate;
19542     },
19543
19544     
19545     remove: function() 
19546     {
19547         this.picker().remove();
19548     },
19549     
19550     validateValue : function(value)
19551     {
19552         if(this.getVisibilityEl().hasClass('hidden')){
19553             return true;
19554         }
19555         
19556         if(value.length < 1)  {
19557             if(this.allowBlank){
19558                 return true;
19559             }
19560             return false;
19561         }
19562         
19563         if(value.length < this.minLength){
19564             return false;
19565         }
19566         if(value.length > this.maxLength){
19567             return false;
19568         }
19569         if(this.vtype){
19570             var vt = Roo.form.VTypes;
19571             if(!vt[this.vtype](value, this)){
19572                 return false;
19573             }
19574         }
19575         if(typeof this.validator == "function"){
19576             var msg = this.validator(value);
19577             if(msg !== true){
19578                 return false;
19579             }
19580         }
19581         
19582         if(this.regex && !this.regex.test(value)){
19583             return false;
19584         }
19585         
19586         if(typeof(this.parseDate(value)) == 'undefined'){
19587             return false;
19588         }
19589         
19590         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19591             return false;
19592         }      
19593         
19594         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19595             return false;
19596         } 
19597         
19598         
19599         return true;
19600     },
19601     
19602     reset : function()
19603     {
19604         this.date = this.viewDate = '';
19605         
19606         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19607     }
19608    
19609 });
19610
19611 Roo.apply(Roo.bootstrap.DateField,  {
19612     
19613     head : {
19614         tag: 'thead',
19615         cn: [
19616         {
19617             tag: 'tr',
19618             cn: [
19619             {
19620                 tag: 'th',
19621                 cls: 'prev',
19622                 html: '<i class="fa fa-arrow-left"/>'
19623             },
19624             {
19625                 tag: 'th',
19626                 cls: 'switch',
19627                 colspan: '5'
19628             },
19629             {
19630                 tag: 'th',
19631                 cls: 'next',
19632                 html: '<i class="fa fa-arrow-right"/>'
19633             }
19634
19635             ]
19636         }
19637         ]
19638     },
19639     
19640     content : {
19641         tag: 'tbody',
19642         cn: [
19643         {
19644             tag: 'tr',
19645             cn: [
19646             {
19647                 tag: 'td',
19648                 colspan: '7'
19649             }
19650             ]
19651         }
19652         ]
19653     },
19654     
19655     footer : {
19656         tag: 'tfoot',
19657         cn: [
19658         {
19659             tag: 'tr',
19660             cn: [
19661             {
19662                 tag: 'th',
19663                 colspan: '7',
19664                 cls: 'today'
19665             }
19666                     
19667             ]
19668         }
19669         ]
19670     },
19671     
19672     dates:{
19673         en: {
19674             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19675             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19676             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19677             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19678             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19679             today: "Today"
19680         }
19681     },
19682     
19683     modes: [
19684     {
19685         clsName: 'days',
19686         navFnc: 'Month',
19687         navStep: 1
19688     },
19689     {
19690         clsName: 'months',
19691         navFnc: 'FullYear',
19692         navStep: 1
19693     },
19694     {
19695         clsName: 'years',
19696         navFnc: 'FullYear',
19697         navStep: 10
19698     }]
19699 });
19700
19701 Roo.apply(Roo.bootstrap.DateField,  {
19702   
19703     template : {
19704         tag: 'div',
19705         cls: 'datepicker dropdown-menu roo-dynamic',
19706         cn: [
19707         {
19708             tag: 'div',
19709             cls: 'datepicker-days',
19710             cn: [
19711             {
19712                 tag: 'table',
19713                 cls: 'table-condensed',
19714                 cn:[
19715                 Roo.bootstrap.DateField.head,
19716                 {
19717                     tag: 'tbody'
19718                 },
19719                 Roo.bootstrap.DateField.footer
19720                 ]
19721             }
19722             ]
19723         },
19724         {
19725             tag: 'div',
19726             cls: 'datepicker-months',
19727             cn: [
19728             {
19729                 tag: 'table',
19730                 cls: 'table-condensed',
19731                 cn:[
19732                 Roo.bootstrap.DateField.head,
19733                 Roo.bootstrap.DateField.content,
19734                 Roo.bootstrap.DateField.footer
19735                 ]
19736             }
19737             ]
19738         },
19739         {
19740             tag: 'div',
19741             cls: 'datepicker-years',
19742             cn: [
19743             {
19744                 tag: 'table',
19745                 cls: 'table-condensed',
19746                 cn:[
19747                 Roo.bootstrap.DateField.head,
19748                 Roo.bootstrap.DateField.content,
19749                 Roo.bootstrap.DateField.footer
19750                 ]
19751             }
19752             ]
19753         }
19754         ]
19755     }
19756 });
19757
19758  
19759
19760  /*
19761  * - LGPL
19762  *
19763  * TimeField
19764  * 
19765  */
19766
19767 /**
19768  * @class Roo.bootstrap.TimeField
19769  * @extends Roo.bootstrap.Input
19770  * Bootstrap DateField class
19771  * 
19772  * 
19773  * @constructor
19774  * Create a new TimeField
19775  * @param {Object} config The config object
19776  */
19777
19778 Roo.bootstrap.TimeField = function(config){
19779     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19780     this.addEvents({
19781             /**
19782              * @event show
19783              * Fires when this field show.
19784              * @param {Roo.bootstrap.DateField} thisthis
19785              * @param {Mixed} date The date value
19786              */
19787             show : true,
19788             /**
19789              * @event show
19790              * Fires when this field hide.
19791              * @param {Roo.bootstrap.DateField} this
19792              * @param {Mixed} date The date value
19793              */
19794             hide : true,
19795             /**
19796              * @event select
19797              * Fires when select a date.
19798              * @param {Roo.bootstrap.DateField} this
19799              * @param {Mixed} date The date value
19800              */
19801             select : true
19802         });
19803 };
19804
19805 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19806     
19807     /**
19808      * @cfg {String} format
19809      * The default time format string which can be overriden for localization support.  The format must be
19810      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19811      */
19812     format : "H:i",
19813        
19814     onRender: function(ct, position)
19815     {
19816         
19817         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19818                 
19819         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19820         
19821         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19822         
19823         this.pop = this.picker().select('>.datepicker-time',true).first();
19824         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19825         
19826         this.picker().on('mousedown', this.onMousedown, this);
19827         this.picker().on('click', this.onClick, this);
19828         
19829         this.picker().addClass('datepicker-dropdown');
19830     
19831         this.fillTime();
19832         this.update();
19833             
19834         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19835         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19836         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19837         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19838         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19839         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19840
19841     },
19842     
19843     fireKey: function(e){
19844         if (!this.picker().isVisible()){
19845             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19846                 this.show();
19847             }
19848             return;
19849         }
19850
19851         e.preventDefault();
19852         
19853         switch(e.keyCode){
19854             case 27: // escape
19855                 this.hide();
19856                 break;
19857             case 37: // left
19858             case 39: // right
19859                 this.onTogglePeriod();
19860                 break;
19861             case 38: // up
19862                 this.onIncrementMinutes();
19863                 break;
19864             case 40: // down
19865                 this.onDecrementMinutes();
19866                 break;
19867             case 13: // enter
19868             case 9: // tab
19869                 this.setTime();
19870                 break;
19871         }
19872     },
19873     
19874     onClick: function(e) {
19875         e.stopPropagation();
19876         e.preventDefault();
19877     },
19878     
19879     picker : function()
19880     {
19881         return this.el.select('.datepicker', true).first();
19882     },
19883     
19884     fillTime: function()
19885     {    
19886         var time = this.pop.select('tbody', true).first();
19887         
19888         time.dom.innerHTML = '';
19889         
19890         time.createChild({
19891             tag: 'tr',
19892             cn: [
19893                 {
19894                     tag: 'td',
19895                     cn: [
19896                         {
19897                             tag: 'a',
19898                             href: '#',
19899                             cls: 'btn',
19900                             cn: [
19901                                 {
19902                                     tag: 'span',
19903                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19904                                 }
19905                             ]
19906                         } 
19907                     ]
19908                 },
19909                 {
19910                     tag: 'td',
19911                     cls: 'separator'
19912                 },
19913                 {
19914                     tag: 'td',
19915                     cn: [
19916                         {
19917                             tag: 'a',
19918                             href: '#',
19919                             cls: 'btn',
19920                             cn: [
19921                                 {
19922                                     tag: 'span',
19923                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19924                                 }
19925                             ]
19926                         }
19927                     ]
19928                 },
19929                 {
19930                     tag: 'td',
19931                     cls: 'separator'
19932                 }
19933             ]
19934         });
19935         
19936         time.createChild({
19937             tag: 'tr',
19938             cn: [
19939                 {
19940                     tag: 'td',
19941                     cn: [
19942                         {
19943                             tag: 'span',
19944                             cls: 'timepicker-hour',
19945                             html: '00'
19946                         }  
19947                     ]
19948                 },
19949                 {
19950                     tag: 'td',
19951                     cls: 'separator',
19952                     html: ':'
19953                 },
19954                 {
19955                     tag: 'td',
19956                     cn: [
19957                         {
19958                             tag: 'span',
19959                             cls: 'timepicker-minute',
19960                             html: '00'
19961                         }  
19962                     ]
19963                 },
19964                 {
19965                     tag: 'td',
19966                     cls: 'separator'
19967                 },
19968                 {
19969                     tag: 'td',
19970                     cn: [
19971                         {
19972                             tag: 'button',
19973                             type: 'button',
19974                             cls: 'btn btn-primary period',
19975                             html: 'AM'
19976                             
19977                         }
19978                     ]
19979                 }
19980             ]
19981         });
19982         
19983         time.createChild({
19984             tag: 'tr',
19985             cn: [
19986                 {
19987                     tag: 'td',
19988                     cn: [
19989                         {
19990                             tag: 'a',
19991                             href: '#',
19992                             cls: 'btn',
19993                             cn: [
19994                                 {
19995                                     tag: 'span',
19996                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19997                                 }
19998                             ]
19999                         }
20000                     ]
20001                 },
20002                 {
20003                     tag: 'td',
20004                     cls: 'separator'
20005                 },
20006                 {
20007                     tag: 'td',
20008                     cn: [
20009                         {
20010                             tag: 'a',
20011                             href: '#',
20012                             cls: 'btn',
20013                             cn: [
20014                                 {
20015                                     tag: 'span',
20016                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20017                                 }
20018                             ]
20019                         }
20020                     ]
20021                 },
20022                 {
20023                     tag: 'td',
20024                     cls: 'separator'
20025                 }
20026             ]
20027         });
20028         
20029     },
20030     
20031     update: function()
20032     {
20033         
20034         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20035         
20036         this.fill();
20037     },
20038     
20039     fill: function() 
20040     {
20041         var hours = this.time.getHours();
20042         var minutes = this.time.getMinutes();
20043         var period = 'AM';
20044         
20045         if(hours > 11){
20046             period = 'PM';
20047         }
20048         
20049         if(hours == 0){
20050             hours = 12;
20051         }
20052         
20053         
20054         if(hours > 12){
20055             hours = hours - 12;
20056         }
20057         
20058         if(hours < 10){
20059             hours = '0' + hours;
20060         }
20061         
20062         if(minutes < 10){
20063             minutes = '0' + minutes;
20064         }
20065         
20066         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20067         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20068         this.pop.select('button', true).first().dom.innerHTML = period;
20069         
20070     },
20071     
20072     place: function()
20073     {   
20074         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20075         
20076         var cls = ['bottom'];
20077         
20078         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20079             cls.pop();
20080             cls.push('top');
20081         }
20082         
20083         cls.push('right');
20084         
20085         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20086             cls.pop();
20087             cls.push('left');
20088         }
20089         
20090         this.picker().addClass(cls.join('-'));
20091         
20092         var _this = this;
20093         
20094         Roo.each(cls, function(c){
20095             if(c == 'bottom'){
20096                 _this.picker().setTop(_this.inputEl().getHeight());
20097                 return;
20098             }
20099             if(c == 'top'){
20100                 _this.picker().setTop(0 - _this.picker().getHeight());
20101                 return;
20102             }
20103             
20104             if(c == 'left'){
20105                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20106                 return;
20107             }
20108             if(c == 'right'){
20109                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20110                 return;
20111             }
20112         });
20113         
20114     },
20115   
20116     onFocus : function()
20117     {
20118         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20119         this.show();
20120     },
20121     
20122     onBlur : function()
20123     {
20124         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20125         this.hide();
20126     },
20127     
20128     show : function()
20129     {
20130         this.picker().show();
20131         this.pop.show();
20132         this.update();
20133         this.place();
20134         
20135         this.fireEvent('show', this, this.date);
20136     },
20137     
20138     hide : function()
20139     {
20140         this.picker().hide();
20141         this.pop.hide();
20142         
20143         this.fireEvent('hide', this, this.date);
20144     },
20145     
20146     setTime : function()
20147     {
20148         this.hide();
20149         this.setValue(this.time.format(this.format));
20150         
20151         this.fireEvent('select', this, this.date);
20152         
20153         
20154     },
20155     
20156     onMousedown: function(e){
20157         e.stopPropagation();
20158         e.preventDefault();
20159     },
20160     
20161     onIncrementHours: function()
20162     {
20163         Roo.log('onIncrementHours');
20164         this.time = this.time.add(Date.HOUR, 1);
20165         this.update();
20166         
20167     },
20168     
20169     onDecrementHours: function()
20170     {
20171         Roo.log('onDecrementHours');
20172         this.time = this.time.add(Date.HOUR, -1);
20173         this.update();
20174     },
20175     
20176     onIncrementMinutes: function()
20177     {
20178         Roo.log('onIncrementMinutes');
20179         this.time = this.time.add(Date.MINUTE, 1);
20180         this.update();
20181     },
20182     
20183     onDecrementMinutes: function()
20184     {
20185         Roo.log('onDecrementMinutes');
20186         this.time = this.time.add(Date.MINUTE, -1);
20187         this.update();
20188     },
20189     
20190     onTogglePeriod: function()
20191     {
20192         Roo.log('onTogglePeriod');
20193         this.time = this.time.add(Date.HOUR, 12);
20194         this.update();
20195     }
20196     
20197    
20198 });
20199
20200 Roo.apply(Roo.bootstrap.TimeField,  {
20201     
20202     content : {
20203         tag: 'tbody',
20204         cn: [
20205             {
20206                 tag: 'tr',
20207                 cn: [
20208                 {
20209                     tag: 'td',
20210                     colspan: '7'
20211                 }
20212                 ]
20213             }
20214         ]
20215     },
20216     
20217     footer : {
20218         tag: 'tfoot',
20219         cn: [
20220             {
20221                 tag: 'tr',
20222                 cn: [
20223                 {
20224                     tag: 'th',
20225                     colspan: '7',
20226                     cls: '',
20227                     cn: [
20228                         {
20229                             tag: 'button',
20230                             cls: 'btn btn-info ok',
20231                             html: 'OK'
20232                         }
20233                     ]
20234                 }
20235
20236                 ]
20237             }
20238         ]
20239     }
20240 });
20241
20242 Roo.apply(Roo.bootstrap.TimeField,  {
20243   
20244     template : {
20245         tag: 'div',
20246         cls: 'datepicker dropdown-menu',
20247         cn: [
20248             {
20249                 tag: 'div',
20250                 cls: 'datepicker-time',
20251                 cn: [
20252                 {
20253                     tag: 'table',
20254                     cls: 'table-condensed',
20255                     cn:[
20256                     Roo.bootstrap.TimeField.content,
20257                     Roo.bootstrap.TimeField.footer
20258                     ]
20259                 }
20260                 ]
20261             }
20262         ]
20263     }
20264 });
20265
20266  
20267
20268  /*
20269  * - LGPL
20270  *
20271  * MonthField
20272  * 
20273  */
20274
20275 /**
20276  * @class Roo.bootstrap.MonthField
20277  * @extends Roo.bootstrap.Input
20278  * Bootstrap MonthField class
20279  * 
20280  * @cfg {String} language default en
20281  * 
20282  * @constructor
20283  * Create a new MonthField
20284  * @param {Object} config The config object
20285  */
20286
20287 Roo.bootstrap.MonthField = function(config){
20288     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20289     
20290     this.addEvents({
20291         /**
20292          * @event show
20293          * Fires when this field show.
20294          * @param {Roo.bootstrap.MonthField} this
20295          * @param {Mixed} date The date value
20296          */
20297         show : true,
20298         /**
20299          * @event show
20300          * Fires when this field hide.
20301          * @param {Roo.bootstrap.MonthField} this
20302          * @param {Mixed} date The date value
20303          */
20304         hide : true,
20305         /**
20306          * @event select
20307          * Fires when select a date.
20308          * @param {Roo.bootstrap.MonthField} this
20309          * @param {String} oldvalue The old value
20310          * @param {String} newvalue The new value
20311          */
20312         select : true
20313     });
20314 };
20315
20316 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20317     
20318     onRender: function(ct, position)
20319     {
20320         
20321         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20322         
20323         this.language = this.language || 'en';
20324         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20325         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20326         
20327         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20328         this.isInline = false;
20329         this.isInput = true;
20330         this.component = this.el.select('.add-on', true).first() || false;
20331         this.component = (this.component && this.component.length === 0) ? false : this.component;
20332         this.hasInput = this.component && this.inputEL().length;
20333         
20334         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20335         
20336         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20337         
20338         this.picker().on('mousedown', this.onMousedown, this);
20339         this.picker().on('click', this.onClick, this);
20340         
20341         this.picker().addClass('datepicker-dropdown');
20342         
20343         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20344             v.setStyle('width', '189px');
20345         });
20346         
20347         this.fillMonths();
20348         
20349         this.update();
20350         
20351         if(this.isInline) {
20352             this.show();
20353         }
20354         
20355     },
20356     
20357     setValue: function(v, suppressEvent)
20358     {   
20359         var o = this.getValue();
20360         
20361         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20362         
20363         this.update();
20364
20365         if(suppressEvent !== true){
20366             this.fireEvent('select', this, o, v);
20367         }
20368         
20369     },
20370     
20371     getValue: function()
20372     {
20373         return this.value;
20374     },
20375     
20376     onClick: function(e) 
20377     {
20378         e.stopPropagation();
20379         e.preventDefault();
20380         
20381         var target = e.getTarget();
20382         
20383         if(target.nodeName.toLowerCase() === 'i'){
20384             target = Roo.get(target).dom.parentNode;
20385         }
20386         
20387         var nodeName = target.nodeName;
20388         var className = target.className;
20389         var html = target.innerHTML;
20390         
20391         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20392             return;
20393         }
20394         
20395         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20396         
20397         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20398         
20399         this.hide();
20400                         
20401     },
20402     
20403     picker : function()
20404     {
20405         return this.pickerEl;
20406     },
20407     
20408     fillMonths: function()
20409     {    
20410         var i = 0;
20411         var months = this.picker().select('>.datepicker-months td', true).first();
20412         
20413         months.dom.innerHTML = '';
20414         
20415         while (i < 12) {
20416             var month = {
20417                 tag: 'span',
20418                 cls: 'month',
20419                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20420             };
20421             
20422             months.createChild(month);
20423         }
20424         
20425     },
20426     
20427     update: function()
20428     {
20429         var _this = this;
20430         
20431         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20432             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20433         }
20434         
20435         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20436             e.removeClass('active');
20437             
20438             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20439                 e.addClass('active');
20440             }
20441         })
20442     },
20443     
20444     place: function()
20445     {
20446         if(this.isInline) {
20447             return;
20448         }
20449         
20450         this.picker().removeClass(['bottom', 'top']);
20451         
20452         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20453             /*
20454              * place to the top of element!
20455              *
20456              */
20457             
20458             this.picker().addClass('top');
20459             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20460             
20461             return;
20462         }
20463         
20464         this.picker().addClass('bottom');
20465         
20466         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20467     },
20468     
20469     onFocus : function()
20470     {
20471         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20472         this.show();
20473     },
20474     
20475     onBlur : function()
20476     {
20477         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20478         
20479         var d = this.inputEl().getValue();
20480         
20481         this.setValue(d);
20482                 
20483         this.hide();
20484     },
20485     
20486     show : function()
20487     {
20488         this.picker().show();
20489         this.picker().select('>.datepicker-months', true).first().show();
20490         this.update();
20491         this.place();
20492         
20493         this.fireEvent('show', this, this.date);
20494     },
20495     
20496     hide : function()
20497     {
20498         if(this.isInline) {
20499             return;
20500         }
20501         this.picker().hide();
20502         this.fireEvent('hide', this, this.date);
20503         
20504     },
20505     
20506     onMousedown: function(e)
20507     {
20508         e.stopPropagation();
20509         e.preventDefault();
20510     },
20511     
20512     keyup: function(e)
20513     {
20514         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20515         this.update();
20516     },
20517
20518     fireKey: function(e)
20519     {
20520         if (!this.picker().isVisible()){
20521             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20522                 this.show();
20523             }
20524             return;
20525         }
20526         
20527         var dir;
20528         
20529         switch(e.keyCode){
20530             case 27: // escape
20531                 this.hide();
20532                 e.preventDefault();
20533                 break;
20534             case 37: // left
20535             case 39: // right
20536                 dir = e.keyCode == 37 ? -1 : 1;
20537                 
20538                 this.vIndex = this.vIndex + dir;
20539                 
20540                 if(this.vIndex < 0){
20541                     this.vIndex = 0;
20542                 }
20543                 
20544                 if(this.vIndex > 11){
20545                     this.vIndex = 11;
20546                 }
20547                 
20548                 if(isNaN(this.vIndex)){
20549                     this.vIndex = 0;
20550                 }
20551                 
20552                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20553                 
20554                 break;
20555             case 38: // up
20556             case 40: // down
20557                 
20558                 dir = e.keyCode == 38 ? -1 : 1;
20559                 
20560                 this.vIndex = this.vIndex + dir * 4;
20561                 
20562                 if(this.vIndex < 0){
20563                     this.vIndex = 0;
20564                 }
20565                 
20566                 if(this.vIndex > 11){
20567                     this.vIndex = 11;
20568                 }
20569                 
20570                 if(isNaN(this.vIndex)){
20571                     this.vIndex = 0;
20572                 }
20573                 
20574                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20575                 break;
20576                 
20577             case 13: // enter
20578                 
20579                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20580                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20581                 }
20582                 
20583                 this.hide();
20584                 e.preventDefault();
20585                 break;
20586             case 9: // tab
20587                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20588                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20589                 }
20590                 this.hide();
20591                 break;
20592             case 16: // shift
20593             case 17: // ctrl
20594             case 18: // alt
20595                 break;
20596             default :
20597                 this.hide();
20598                 
20599         }
20600     },
20601     
20602     remove: function() 
20603     {
20604         this.picker().remove();
20605     }
20606    
20607 });
20608
20609 Roo.apply(Roo.bootstrap.MonthField,  {
20610     
20611     content : {
20612         tag: 'tbody',
20613         cn: [
20614         {
20615             tag: 'tr',
20616             cn: [
20617             {
20618                 tag: 'td',
20619                 colspan: '7'
20620             }
20621             ]
20622         }
20623         ]
20624     },
20625     
20626     dates:{
20627         en: {
20628             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20629             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20630         }
20631     }
20632 });
20633
20634 Roo.apply(Roo.bootstrap.MonthField,  {
20635   
20636     template : {
20637         tag: 'div',
20638         cls: 'datepicker dropdown-menu roo-dynamic',
20639         cn: [
20640             {
20641                 tag: 'div',
20642                 cls: 'datepicker-months',
20643                 cn: [
20644                 {
20645                     tag: 'table',
20646                     cls: 'table-condensed',
20647                     cn:[
20648                         Roo.bootstrap.DateField.content
20649                     ]
20650                 }
20651                 ]
20652             }
20653         ]
20654     }
20655 });
20656
20657  
20658
20659  
20660  /*
20661  * - LGPL
20662  *
20663  * CheckBox
20664  * 
20665  */
20666
20667 /**
20668  * @class Roo.bootstrap.CheckBox
20669  * @extends Roo.bootstrap.Input
20670  * Bootstrap CheckBox class
20671  * 
20672  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20673  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20674  * @cfg {String} boxLabel The text that appears beside the checkbox
20675  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20676  * @cfg {Boolean} checked initnal the element
20677  * @cfg {Boolean} inline inline the element (default false)
20678  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20679  * @cfg {String} tooltip label tooltip
20680  * 
20681  * @constructor
20682  * Create a new CheckBox
20683  * @param {Object} config The config object
20684  */
20685
20686 Roo.bootstrap.CheckBox = function(config){
20687     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20688    
20689     this.addEvents({
20690         /**
20691         * @event check
20692         * Fires when the element is checked or unchecked.
20693         * @param {Roo.bootstrap.CheckBox} this This input
20694         * @param {Boolean} checked The new checked value
20695         */
20696        check : true,
20697        /**
20698         * @event click
20699         * Fires when the element is click.
20700         * @param {Roo.bootstrap.CheckBox} this This input
20701         */
20702        click : true
20703     });
20704     
20705 };
20706
20707 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20708   
20709     inputType: 'checkbox',
20710     inputValue: 1,
20711     valueOff: 0,
20712     boxLabel: false,
20713     checked: false,
20714     weight : false,
20715     inline: false,
20716     tooltip : '',
20717     
20718     getAutoCreate : function()
20719     {
20720         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20721         
20722         var id = Roo.id();
20723         
20724         var cfg = {};
20725         
20726         cfg.cls = 'form-group ' + this.inputType; //input-group
20727         
20728         if(this.inline){
20729             cfg.cls += ' ' + this.inputType + '-inline';
20730         }
20731         
20732         var input =  {
20733             tag: 'input',
20734             id : id,
20735             type : this.inputType,
20736             value : this.inputValue,
20737             cls : 'roo-' + this.inputType, //'form-box',
20738             placeholder : this.placeholder || ''
20739             
20740         };
20741         
20742         if(this.inputType != 'radio'){
20743             var hidden =  {
20744                 tag: 'input',
20745                 type : 'hidden',
20746                 cls : 'roo-hidden-value',
20747                 value : this.checked ? this.inputValue : this.valueOff
20748             };
20749         }
20750         
20751             
20752         if (this.weight) { // Validity check?
20753             cfg.cls += " " + this.inputType + "-" + this.weight;
20754         }
20755         
20756         if (this.disabled) {
20757             input.disabled=true;
20758         }
20759         
20760         if(this.checked){
20761             input.checked = this.checked;
20762         }
20763         
20764         if (this.name) {
20765             
20766             input.name = this.name;
20767             
20768             if(this.inputType != 'radio'){
20769                 hidden.name = this.name;
20770                 input.name = '_hidden_' + this.name;
20771             }
20772         }
20773         
20774         if (this.size) {
20775             input.cls += ' input-' + this.size;
20776         }
20777         
20778         var settings=this;
20779         
20780         ['xs','sm','md','lg'].map(function(size){
20781             if (settings[size]) {
20782                 cfg.cls += ' col-' + size + '-' + settings[size];
20783             }
20784         });
20785         
20786         var inputblock = input;
20787          
20788         if (this.before || this.after) {
20789             
20790             inputblock = {
20791                 cls : 'input-group',
20792                 cn :  [] 
20793             };
20794             
20795             if (this.before) {
20796                 inputblock.cn.push({
20797                     tag :'span',
20798                     cls : 'input-group-addon',
20799                     html : this.before
20800                 });
20801             }
20802             
20803             inputblock.cn.push(input);
20804             
20805             if(this.inputType != 'radio'){
20806                 inputblock.cn.push(hidden);
20807             }
20808             
20809             if (this.after) {
20810                 inputblock.cn.push({
20811                     tag :'span',
20812                     cls : 'input-group-addon',
20813                     html : this.after
20814                 });
20815             }
20816             
20817         }
20818         
20819         if (align ==='left' && this.fieldLabel.length) {
20820 //                Roo.log("left and has label");
20821             cfg.cn = [
20822                 {
20823                     tag: 'label',
20824                     'for' :  id,
20825                     cls : 'control-label',
20826                     html : this.fieldLabel
20827                 },
20828                 {
20829                     cls : "", 
20830                     cn: [
20831                         inputblock
20832                     ]
20833                 }
20834             ];
20835             
20836             if(this.labelWidth > 12){
20837                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20838             }
20839             
20840             if(this.labelWidth < 13 && this.labelmd == 0){
20841                 this.labelmd = this.labelWidth;
20842             }
20843             
20844             if(this.labellg > 0){
20845                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20846                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20847             }
20848             
20849             if(this.labelmd > 0){
20850                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20851                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20852             }
20853             
20854             if(this.labelsm > 0){
20855                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20856                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20857             }
20858             
20859             if(this.labelxs > 0){
20860                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20861                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20862             }
20863             
20864         } else if ( this.fieldLabel.length) {
20865 //                Roo.log(" label");
20866                 cfg.cn = [
20867                    
20868                     {
20869                         tag: this.boxLabel ? 'span' : 'label',
20870                         'for': id,
20871                         cls: 'control-label box-input-label',
20872                         //cls : 'input-group-addon',
20873                         html : this.fieldLabel
20874                     },
20875                     
20876                     inputblock
20877                     
20878                 ];
20879
20880         } else {
20881             
20882 //                Roo.log(" no label && no align");
20883                 cfg.cn = [  inputblock ] ;
20884                 
20885                 
20886         }
20887         
20888         if(this.boxLabel){
20889              var boxLabelCfg = {
20890                 tag: 'label',
20891                 //'for': id, // box label is handled by onclick - so no for...
20892                 cls: 'box-label',
20893                 html: this.boxLabel
20894             };
20895             
20896             if(this.tooltip){
20897                 boxLabelCfg.tooltip = this.tooltip;
20898             }
20899              
20900             cfg.cn.push(boxLabelCfg);
20901         }
20902         
20903         if(this.inputType != 'radio'){
20904             cfg.cn.push(hidden);
20905         }
20906         
20907         return cfg;
20908         
20909     },
20910     
20911     /**
20912      * return the real input element.
20913      */
20914     inputEl: function ()
20915     {
20916         return this.el.select('input.roo-' + this.inputType,true).first();
20917     },
20918     hiddenEl: function ()
20919     {
20920         return this.el.select('input.roo-hidden-value',true).first();
20921     },
20922     
20923     labelEl: function()
20924     {
20925         return this.el.select('label.control-label',true).first();
20926     },
20927     /* depricated... */
20928     
20929     label: function()
20930     {
20931         return this.labelEl();
20932     },
20933     
20934     boxLabelEl: function()
20935     {
20936         return this.el.select('label.box-label',true).first();
20937     },
20938     
20939     initEvents : function()
20940     {
20941 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20942         
20943         this.inputEl().on('click', this.onClick,  this);
20944         
20945         if (this.boxLabel) { 
20946             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20947         }
20948         
20949         this.startValue = this.getValue();
20950         
20951         if(this.groupId){
20952             Roo.bootstrap.CheckBox.register(this);
20953         }
20954     },
20955     
20956     onClick : function(e)
20957     {   
20958         if(this.fireEvent('click', this, e) !== false){
20959             this.setChecked(!this.checked);
20960         }
20961         
20962     },
20963     
20964     setChecked : function(state,suppressEvent)
20965     {
20966         this.startValue = this.getValue();
20967
20968         if(this.inputType == 'radio'){
20969             
20970             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20971                 e.dom.checked = false;
20972             });
20973             
20974             this.inputEl().dom.checked = true;
20975             
20976             this.inputEl().dom.value = this.inputValue;
20977             
20978             if(suppressEvent !== true){
20979                 this.fireEvent('check', this, true);
20980             }
20981             
20982             this.validate();
20983             
20984             return;
20985         }
20986         
20987         this.checked = state;
20988         
20989         this.inputEl().dom.checked = state;
20990         
20991         
20992         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20993         
20994         if(suppressEvent !== true){
20995             this.fireEvent('check', this, state);
20996         }
20997         
20998         this.validate();
20999     },
21000     
21001     getValue : function()
21002     {
21003         if(this.inputType == 'radio'){
21004             return this.getGroupValue();
21005         }
21006         
21007         return this.hiddenEl().dom.value;
21008         
21009     },
21010     
21011     getGroupValue : function()
21012     {
21013         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21014             return '';
21015         }
21016         
21017         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21018     },
21019     
21020     setValue : function(v,suppressEvent)
21021     {
21022         if(this.inputType == 'radio'){
21023             this.setGroupValue(v, suppressEvent);
21024             return;
21025         }
21026         
21027         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21028         
21029         this.validate();
21030     },
21031     
21032     setGroupValue : function(v, suppressEvent)
21033     {
21034         this.startValue = this.getValue();
21035         
21036         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21037             e.dom.checked = false;
21038             
21039             if(e.dom.value == v){
21040                 e.dom.checked = true;
21041             }
21042         });
21043         
21044         if(suppressEvent !== true){
21045             this.fireEvent('check', this, true);
21046         }
21047
21048         this.validate();
21049         
21050         return;
21051     },
21052     
21053     validate : function()
21054     {
21055         if(this.getVisibilityEl().hasClass('hidden')){
21056             return true;
21057         }
21058         
21059         if(
21060                 this.disabled || 
21061                 (this.inputType == 'radio' && this.validateRadio()) ||
21062                 (this.inputType == 'checkbox' && this.validateCheckbox())
21063         ){
21064             this.markValid();
21065             return true;
21066         }
21067         
21068         this.markInvalid();
21069         return false;
21070     },
21071     
21072     validateRadio : function()
21073     {
21074         if(this.getVisibilityEl().hasClass('hidden')){
21075             return true;
21076         }
21077         
21078         if(this.allowBlank){
21079             return true;
21080         }
21081         
21082         var valid = false;
21083         
21084         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21085             if(!e.dom.checked){
21086                 return;
21087             }
21088             
21089             valid = true;
21090             
21091             return false;
21092         });
21093         
21094         return valid;
21095     },
21096     
21097     validateCheckbox : function()
21098     {
21099         if(!this.groupId){
21100             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21101             //return (this.getValue() == this.inputValue) ? true : false;
21102         }
21103         
21104         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21105         
21106         if(!group){
21107             return false;
21108         }
21109         
21110         var r = false;
21111         
21112         for(var i in group){
21113             if(group[i].el.isVisible(true)){
21114                 r = false;
21115                 break;
21116             }
21117             
21118             r = true;
21119         }
21120         
21121         for(var i in group){
21122             if(r){
21123                 break;
21124             }
21125             
21126             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21127         }
21128         
21129         return r;
21130     },
21131     
21132     /**
21133      * Mark this field as valid
21134      */
21135     markValid : function()
21136     {
21137         var _this = this;
21138         
21139         this.fireEvent('valid', this);
21140         
21141         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21142         
21143         if(this.groupId){
21144             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21145         }
21146         
21147         if(label){
21148             label.markValid();
21149         }
21150
21151         if(this.inputType == 'radio'){
21152             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21153                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21154                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21155             });
21156             
21157             return;
21158         }
21159
21160         if(!this.groupId){
21161             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21162             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21163             return;
21164         }
21165         
21166         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21167         
21168         if(!group){
21169             return;
21170         }
21171         
21172         for(var i in group){
21173             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21174             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21175         }
21176     },
21177     
21178      /**
21179      * Mark this field as invalid
21180      * @param {String} msg The validation message
21181      */
21182     markInvalid : function(msg)
21183     {
21184         if(this.allowBlank){
21185             return;
21186         }
21187         
21188         var _this = this;
21189         
21190         this.fireEvent('invalid', this, msg);
21191         
21192         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21193         
21194         if(this.groupId){
21195             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21196         }
21197         
21198         if(label){
21199             label.markInvalid();
21200         }
21201             
21202         if(this.inputType == 'radio'){
21203             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21204                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21205                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21206             });
21207             
21208             return;
21209         }
21210         
21211         if(!this.groupId){
21212             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21213             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21214             return;
21215         }
21216         
21217         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21218         
21219         if(!group){
21220             return;
21221         }
21222         
21223         for(var i in group){
21224             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21225             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21226         }
21227         
21228     },
21229     
21230     clearInvalid : function()
21231     {
21232         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21233         
21234         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21235         
21236         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21237         
21238         if (label && label.iconEl) {
21239             label.iconEl.removeClass(label.validClass);
21240             label.iconEl.removeClass(label.invalidClass);
21241         }
21242     },
21243     
21244     disable : function()
21245     {
21246         if(this.inputType != 'radio'){
21247             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21248             return;
21249         }
21250         
21251         var _this = this;
21252         
21253         if(this.rendered){
21254             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21255                 _this.getActionEl().addClass(this.disabledClass);
21256                 e.dom.disabled = true;
21257             });
21258         }
21259         
21260         this.disabled = true;
21261         this.fireEvent("disable", this);
21262         return this;
21263     },
21264
21265     enable : function()
21266     {
21267         if(this.inputType != 'radio'){
21268             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21269             return;
21270         }
21271         
21272         var _this = this;
21273         
21274         if(this.rendered){
21275             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21276                 _this.getActionEl().removeClass(this.disabledClass);
21277                 e.dom.disabled = false;
21278             });
21279         }
21280         
21281         this.disabled = false;
21282         this.fireEvent("enable", this);
21283         return this;
21284     },
21285     
21286     setBoxLabel : function(v)
21287     {
21288         this.boxLabel = v;
21289         
21290         if(this.rendered){
21291             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21292         }
21293     }
21294
21295 });
21296
21297 Roo.apply(Roo.bootstrap.CheckBox, {
21298     
21299     groups: {},
21300     
21301      /**
21302     * register a CheckBox Group
21303     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21304     */
21305     register : function(checkbox)
21306     {
21307         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21308             this.groups[checkbox.groupId] = {};
21309         }
21310         
21311         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21312             return;
21313         }
21314         
21315         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21316         
21317     },
21318     /**
21319     * fetch a CheckBox Group based on the group ID
21320     * @param {string} the group ID
21321     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21322     */
21323     get: function(groupId) {
21324         if (typeof(this.groups[groupId]) == 'undefined') {
21325             return false;
21326         }
21327         
21328         return this.groups[groupId] ;
21329     }
21330     
21331     
21332 });
21333 /*
21334  * - LGPL
21335  *
21336  * RadioItem
21337  * 
21338  */
21339
21340 /**
21341  * @class Roo.bootstrap.Radio
21342  * @extends Roo.bootstrap.Component
21343  * Bootstrap Radio class
21344  * @cfg {String} boxLabel - the label associated
21345  * @cfg {String} value - the value of radio
21346  * 
21347  * @constructor
21348  * Create a new Radio
21349  * @param {Object} config The config object
21350  */
21351 Roo.bootstrap.Radio = function(config){
21352     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21353     
21354 };
21355
21356 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21357     
21358     boxLabel : '',
21359     
21360     value : '',
21361     
21362     getAutoCreate : function()
21363     {
21364         var cfg = {
21365             tag : 'div',
21366             cls : 'form-group radio',
21367             cn : [
21368                 {
21369                     tag : 'label',
21370                     cls : 'box-label',
21371                     html : this.boxLabel
21372                 }
21373             ]
21374         };
21375         
21376         return cfg;
21377     },
21378     
21379     initEvents : function() 
21380     {
21381         this.parent().register(this);
21382         
21383         this.el.on('click', this.onClick, this);
21384         
21385     },
21386     
21387     onClick : function(e)
21388     {
21389         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21390             this.setChecked(true);
21391         }
21392     },
21393     
21394     setChecked : function(state, suppressEvent)
21395     {
21396         this.parent().setValue(this.value, suppressEvent);
21397         
21398     },
21399     
21400     setBoxLabel : function(v)
21401     {
21402         this.boxLabel = v;
21403         
21404         if(this.rendered){
21405             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21406         }
21407     }
21408     
21409 });
21410  
21411
21412  /*
21413  * - LGPL
21414  *
21415  * Input
21416  * 
21417  */
21418
21419 /**
21420  * @class Roo.bootstrap.SecurePass
21421  * @extends Roo.bootstrap.Input
21422  * Bootstrap SecurePass class
21423  *
21424  * 
21425  * @constructor
21426  * Create a new SecurePass
21427  * @param {Object} config The config object
21428  */
21429  
21430 Roo.bootstrap.SecurePass = function (config) {
21431     // these go here, so the translation tool can replace them..
21432     this.errors = {
21433         PwdEmpty: "Please type a password, and then retype it to confirm.",
21434         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21435         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21436         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21437         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21438         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21439         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21440         TooWeak: "Your password is Too Weak."
21441     },
21442     this.meterLabel = "Password strength:";
21443     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21444     this.meterClass = [
21445         "roo-password-meter-tooweak", 
21446         "roo-password-meter-weak", 
21447         "roo-password-meter-medium", 
21448         "roo-password-meter-strong", 
21449         "roo-password-meter-grey"
21450     ];
21451     
21452     this.errors = {};
21453     
21454     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21455 }
21456
21457 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21458     /**
21459      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21460      * {
21461      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21462      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21463      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21464      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21465      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21466      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21467      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21468      * })
21469      */
21470     // private
21471     
21472     meterWidth: 300,
21473     errorMsg :'',    
21474     errors: false,
21475     imageRoot: '/',
21476     /**
21477      * @cfg {String/Object} Label for the strength meter (defaults to
21478      * 'Password strength:')
21479      */
21480     // private
21481     meterLabel: '',
21482     /**
21483      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21484      * ['Weak', 'Medium', 'Strong'])
21485      */
21486     // private    
21487     pwdStrengths: false,    
21488     // private
21489     strength: 0,
21490     // private
21491     _lastPwd: null,
21492     // private
21493     kCapitalLetter: 0,
21494     kSmallLetter: 1,
21495     kDigit: 2,
21496     kPunctuation: 3,
21497     
21498     insecure: false,
21499     // private
21500     initEvents: function ()
21501     {
21502         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21503
21504         if (this.el.is('input[type=password]') && Roo.isSafari) {
21505             this.el.on('keydown', this.SafariOnKeyDown, this);
21506         }
21507
21508         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21509     },
21510     // private
21511     onRender: function (ct, position)
21512     {
21513         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21514         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21515         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21516
21517         this.trigger.createChild({
21518                    cn: [
21519                     {
21520                     //id: 'PwdMeter',
21521                     tag: 'div',
21522                     cls: 'roo-password-meter-grey col-xs-12',
21523                     style: {
21524                         //width: 0,
21525                         //width: this.meterWidth + 'px'                                                
21526                         }
21527                     },
21528                     {                            
21529                          cls: 'roo-password-meter-text'                          
21530                     }
21531                 ]            
21532         });
21533
21534          
21535         if (this.hideTrigger) {
21536             this.trigger.setDisplayed(false);
21537         }
21538         this.setSize(this.width || '', this.height || '');
21539     },
21540     // private
21541     onDestroy: function ()
21542     {
21543         if (this.trigger) {
21544             this.trigger.removeAllListeners();
21545             this.trigger.remove();
21546         }
21547         if (this.wrap) {
21548             this.wrap.remove();
21549         }
21550         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21551     },
21552     // private
21553     checkStrength: function ()
21554     {
21555         var pwd = this.inputEl().getValue();
21556         if (pwd == this._lastPwd) {
21557             return;
21558         }
21559
21560         var strength;
21561         if (this.ClientSideStrongPassword(pwd)) {
21562             strength = 3;
21563         } else if (this.ClientSideMediumPassword(pwd)) {
21564             strength = 2;
21565         } else if (this.ClientSideWeakPassword(pwd)) {
21566             strength = 1;
21567         } else {
21568             strength = 0;
21569         }
21570         
21571         Roo.log('strength1: ' + strength);
21572         
21573         //var pm = this.trigger.child('div/div/div').dom;
21574         var pm = this.trigger.child('div/div');
21575         pm.removeClass(this.meterClass);
21576         pm.addClass(this.meterClass[strength]);
21577                 
21578         
21579         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21580                 
21581         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21582         
21583         this._lastPwd = pwd;
21584     },
21585     reset: function ()
21586     {
21587         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21588         
21589         this._lastPwd = '';
21590         
21591         var pm = this.trigger.child('div/div');
21592         pm.removeClass(this.meterClass);
21593         pm.addClass('roo-password-meter-grey');        
21594         
21595         
21596         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21597         
21598         pt.innerHTML = '';
21599         this.inputEl().dom.type='password';
21600     },
21601     // private
21602     validateValue: function (value)
21603     {
21604         
21605         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21606             return false;
21607         }
21608         if (value.length == 0) {
21609             if (this.allowBlank) {
21610                 this.clearInvalid();
21611                 return true;
21612             }
21613
21614             this.markInvalid(this.errors.PwdEmpty);
21615             this.errorMsg = this.errors.PwdEmpty;
21616             return false;
21617         }
21618         
21619         if(this.insecure){
21620             return true;
21621         }
21622         
21623         if ('[\x21-\x7e]*'.match(value)) {
21624             this.markInvalid(this.errors.PwdBadChar);
21625             this.errorMsg = this.errors.PwdBadChar;
21626             return false;
21627         }
21628         if (value.length < 6) {
21629             this.markInvalid(this.errors.PwdShort);
21630             this.errorMsg = this.errors.PwdShort;
21631             return false;
21632         }
21633         if (value.length > 16) {
21634             this.markInvalid(this.errors.PwdLong);
21635             this.errorMsg = this.errors.PwdLong;
21636             return false;
21637         }
21638         var strength;
21639         if (this.ClientSideStrongPassword(value)) {
21640             strength = 3;
21641         } else if (this.ClientSideMediumPassword(value)) {
21642             strength = 2;
21643         } else if (this.ClientSideWeakPassword(value)) {
21644             strength = 1;
21645         } else {
21646             strength = 0;
21647         }
21648
21649         
21650         if (strength < 2) {
21651             //this.markInvalid(this.errors.TooWeak);
21652             this.errorMsg = this.errors.TooWeak;
21653             //return false;
21654         }
21655         
21656         
21657         console.log('strength2: ' + strength);
21658         
21659         //var pm = this.trigger.child('div/div/div').dom;
21660         
21661         var pm = this.trigger.child('div/div');
21662         pm.removeClass(this.meterClass);
21663         pm.addClass(this.meterClass[strength]);
21664                 
21665         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21666                 
21667         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21668         
21669         this.errorMsg = ''; 
21670         return true;
21671     },
21672     // private
21673     CharacterSetChecks: function (type)
21674     {
21675         this.type = type;
21676         this.fResult = false;
21677     },
21678     // private
21679     isctype: function (character, type)
21680     {
21681         switch (type) {  
21682             case this.kCapitalLetter:
21683                 if (character >= 'A' && character <= 'Z') {
21684                     return true;
21685                 }
21686                 break;
21687             
21688             case this.kSmallLetter:
21689                 if (character >= 'a' && character <= 'z') {
21690                     return true;
21691                 }
21692                 break;
21693             
21694             case this.kDigit:
21695                 if (character >= '0' && character <= '9') {
21696                     return true;
21697                 }
21698                 break;
21699             
21700             case this.kPunctuation:
21701                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21702                     return true;
21703                 }
21704                 break;
21705             
21706             default:
21707                 return false;
21708         }
21709
21710     },
21711     // private
21712     IsLongEnough: function (pwd, size)
21713     {
21714         return !(pwd == null || isNaN(size) || pwd.length < size);
21715     },
21716     // private
21717     SpansEnoughCharacterSets: function (word, nb)
21718     {
21719         if (!this.IsLongEnough(word, nb))
21720         {
21721             return false;
21722         }
21723
21724         var characterSetChecks = new Array(
21725             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21726             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21727         );
21728         
21729         for (var index = 0; index < word.length; ++index) {
21730             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21731                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21732                     characterSetChecks[nCharSet].fResult = true;
21733                     break;
21734                 }
21735             }
21736         }
21737
21738         var nCharSets = 0;
21739         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21740             if (characterSetChecks[nCharSet].fResult) {
21741                 ++nCharSets;
21742             }
21743         }
21744
21745         if (nCharSets < nb) {
21746             return false;
21747         }
21748         return true;
21749     },
21750     // private
21751     ClientSideStrongPassword: function (pwd)
21752     {
21753         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21754     },
21755     // private
21756     ClientSideMediumPassword: function (pwd)
21757     {
21758         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21759     },
21760     // private
21761     ClientSideWeakPassword: function (pwd)
21762     {
21763         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21764     }
21765           
21766 })//<script type="text/javascript">
21767
21768 /*
21769  * Based  Ext JS Library 1.1.1
21770  * Copyright(c) 2006-2007, Ext JS, LLC.
21771  * LGPL
21772  *
21773  */
21774  
21775 /**
21776  * @class Roo.HtmlEditorCore
21777  * @extends Roo.Component
21778  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21779  *
21780  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21781  */
21782
21783 Roo.HtmlEditorCore = function(config){
21784     
21785     
21786     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21787     
21788     
21789     this.addEvents({
21790         /**
21791          * @event initialize
21792          * Fires when the editor is fully initialized (including the iframe)
21793          * @param {Roo.HtmlEditorCore} this
21794          */
21795         initialize: true,
21796         /**
21797          * @event activate
21798          * Fires when the editor is first receives the focus. Any insertion must wait
21799          * until after this event.
21800          * @param {Roo.HtmlEditorCore} this
21801          */
21802         activate: true,
21803          /**
21804          * @event beforesync
21805          * Fires before the textarea is updated with content from the editor iframe. Return false
21806          * to cancel the sync.
21807          * @param {Roo.HtmlEditorCore} this
21808          * @param {String} html
21809          */
21810         beforesync: true,
21811          /**
21812          * @event beforepush
21813          * Fires before the iframe editor is updated with content from the textarea. Return false
21814          * to cancel the push.
21815          * @param {Roo.HtmlEditorCore} this
21816          * @param {String} html
21817          */
21818         beforepush: true,
21819          /**
21820          * @event sync
21821          * Fires when the textarea is updated with content from the editor iframe.
21822          * @param {Roo.HtmlEditorCore} this
21823          * @param {String} html
21824          */
21825         sync: true,
21826          /**
21827          * @event push
21828          * Fires when the iframe editor is updated with content from the textarea.
21829          * @param {Roo.HtmlEditorCore} this
21830          * @param {String} html
21831          */
21832         push: true,
21833         
21834         /**
21835          * @event editorevent
21836          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21837          * @param {Roo.HtmlEditorCore} this
21838          */
21839         editorevent: true
21840         
21841     });
21842     
21843     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21844     
21845     // defaults : white / black...
21846     this.applyBlacklists();
21847     
21848     
21849     
21850 };
21851
21852
21853 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21854
21855
21856      /**
21857      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21858      */
21859     
21860     owner : false,
21861     
21862      /**
21863      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21864      *                        Roo.resizable.
21865      */
21866     resizable : false,
21867      /**
21868      * @cfg {Number} height (in pixels)
21869      */   
21870     height: 300,
21871    /**
21872      * @cfg {Number} width (in pixels)
21873      */   
21874     width: 500,
21875     
21876     /**
21877      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21878      * 
21879      */
21880     stylesheets: false,
21881     
21882     // id of frame..
21883     frameId: false,
21884     
21885     // private properties
21886     validationEvent : false,
21887     deferHeight: true,
21888     initialized : false,
21889     activated : false,
21890     sourceEditMode : false,
21891     onFocus : Roo.emptyFn,
21892     iframePad:3,
21893     hideMode:'offsets',
21894     
21895     clearUp: true,
21896     
21897     // blacklist + whitelisted elements..
21898     black: false,
21899     white: false,
21900      
21901     bodyCls : '',
21902
21903     /**
21904      * Protected method that will not generally be called directly. It
21905      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21906      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21907      */
21908     getDocMarkup : function(){
21909         // body styles..
21910         var st = '';
21911         
21912         // inherit styels from page...?? 
21913         if (this.stylesheets === false) {
21914             
21915             Roo.get(document.head).select('style').each(function(node) {
21916                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21917             });
21918             
21919             Roo.get(document.head).select('link').each(function(node) { 
21920                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21921             });
21922             
21923         } else if (!this.stylesheets.length) {
21924                 // simple..
21925                 st = '<style type="text/css">' +
21926                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21927                    '</style>';
21928         } else { 
21929             st = '<style type="text/css">' +
21930                     this.stylesheets +
21931                 '</style>';
21932         }
21933         
21934         st +=  '<style type="text/css">' +
21935             'IMG { cursor: pointer } ' +
21936         '</style>';
21937
21938         var cls = 'roo-htmleditor-body';
21939         
21940         if(this.bodyCls.length){
21941             cls += ' ' + this.bodyCls;
21942         }
21943         
21944         return '<html><head>' + st  +
21945             //<style type="text/css">' +
21946             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21947             //'</style>' +
21948             ' </head><body class="' +  cls + '"></body></html>';
21949     },
21950
21951     // private
21952     onRender : function(ct, position)
21953     {
21954         var _t = this;
21955         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21956         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21957         
21958         
21959         this.el.dom.style.border = '0 none';
21960         this.el.dom.setAttribute('tabIndex', -1);
21961         this.el.addClass('x-hidden hide');
21962         
21963         
21964         
21965         if(Roo.isIE){ // fix IE 1px bogus margin
21966             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21967         }
21968        
21969         
21970         this.frameId = Roo.id();
21971         
21972          
21973         
21974         var iframe = this.owner.wrap.createChild({
21975             tag: 'iframe',
21976             cls: 'form-control', // bootstrap..
21977             id: this.frameId,
21978             name: this.frameId,
21979             frameBorder : 'no',
21980             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21981         }, this.el
21982         );
21983         
21984         
21985         this.iframe = iframe.dom;
21986
21987          this.assignDocWin();
21988         
21989         this.doc.designMode = 'on';
21990        
21991         this.doc.open();
21992         this.doc.write(this.getDocMarkup());
21993         this.doc.close();
21994
21995         
21996         var task = { // must defer to wait for browser to be ready
21997             run : function(){
21998                 //console.log("run task?" + this.doc.readyState);
21999                 this.assignDocWin();
22000                 if(this.doc.body || this.doc.readyState == 'complete'){
22001                     try {
22002                         this.doc.designMode="on";
22003                     } catch (e) {
22004                         return;
22005                     }
22006                     Roo.TaskMgr.stop(task);
22007                     this.initEditor.defer(10, this);
22008                 }
22009             },
22010             interval : 10,
22011             duration: 10000,
22012             scope: this
22013         };
22014         Roo.TaskMgr.start(task);
22015
22016     },
22017
22018     // private
22019     onResize : function(w, h)
22020     {
22021          Roo.log('resize: ' +w + ',' + h );
22022         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22023         if(!this.iframe){
22024             return;
22025         }
22026         if(typeof w == 'number'){
22027             
22028             this.iframe.style.width = w + 'px';
22029         }
22030         if(typeof h == 'number'){
22031             
22032             this.iframe.style.height = h + 'px';
22033             if(this.doc){
22034                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22035             }
22036         }
22037         
22038     },
22039
22040     /**
22041      * Toggles the editor between standard and source edit mode.
22042      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22043      */
22044     toggleSourceEdit : function(sourceEditMode){
22045         
22046         this.sourceEditMode = sourceEditMode === true;
22047         
22048         if(this.sourceEditMode){
22049  
22050             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22051             
22052         }else{
22053             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22054             //this.iframe.className = '';
22055             this.deferFocus();
22056         }
22057         //this.setSize(this.owner.wrap.getSize());
22058         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22059     },
22060
22061     
22062   
22063
22064     /**
22065      * Protected method that will not generally be called directly. If you need/want
22066      * custom HTML cleanup, this is the method you should override.
22067      * @param {String} html The HTML to be cleaned
22068      * return {String} The cleaned HTML
22069      */
22070     cleanHtml : function(html){
22071         html = String(html);
22072         if(html.length > 5){
22073             if(Roo.isSafari){ // strip safari nonsense
22074                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22075             }
22076         }
22077         if(html == '&nbsp;'){
22078             html = '';
22079         }
22080         return html;
22081     },
22082
22083     /**
22084      * HTML Editor -> Textarea
22085      * Protected method that will not generally be called directly. Syncs the contents
22086      * of the editor iframe with the textarea.
22087      */
22088     syncValue : function(){
22089         if(this.initialized){
22090             var bd = (this.doc.body || this.doc.documentElement);
22091             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22092             var html = bd.innerHTML;
22093             if(Roo.isSafari){
22094                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22095                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22096                 if(m && m[1]){
22097                     html = '<div style="'+m[0]+'">' + html + '</div>';
22098                 }
22099             }
22100             html = this.cleanHtml(html);
22101             // fix up the special chars.. normaly like back quotes in word...
22102             // however we do not want to do this with chinese..
22103             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22104                 var cc = b.charCodeAt();
22105                 if (
22106                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22107                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22108                     (cc >= 0xf900 && cc < 0xfb00 )
22109                 ) {
22110                         return b;
22111                 }
22112                 return "&#"+cc+";" 
22113             });
22114             if(this.owner.fireEvent('beforesync', this, html) !== false){
22115                 this.el.dom.value = html;
22116                 this.owner.fireEvent('sync', this, html);
22117             }
22118         }
22119     },
22120
22121     /**
22122      * Protected method that will not generally be called directly. Pushes the value of the textarea
22123      * into the iframe editor.
22124      */
22125     pushValue : function(){
22126         if(this.initialized){
22127             var v = this.el.dom.value.trim();
22128             
22129 //            if(v.length < 1){
22130 //                v = '&#160;';
22131 //            }
22132             
22133             if(this.owner.fireEvent('beforepush', this, v) !== false){
22134                 var d = (this.doc.body || this.doc.documentElement);
22135                 d.innerHTML = v;
22136                 this.cleanUpPaste();
22137                 this.el.dom.value = d.innerHTML;
22138                 this.owner.fireEvent('push', this, v);
22139             }
22140         }
22141     },
22142
22143     // private
22144     deferFocus : function(){
22145         this.focus.defer(10, this);
22146     },
22147
22148     // doc'ed in Field
22149     focus : function(){
22150         if(this.win && !this.sourceEditMode){
22151             this.win.focus();
22152         }else{
22153             this.el.focus();
22154         }
22155     },
22156     
22157     assignDocWin: function()
22158     {
22159         var iframe = this.iframe;
22160         
22161          if(Roo.isIE){
22162             this.doc = iframe.contentWindow.document;
22163             this.win = iframe.contentWindow;
22164         } else {
22165 //            if (!Roo.get(this.frameId)) {
22166 //                return;
22167 //            }
22168 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22169 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22170             
22171             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22172                 return;
22173             }
22174             
22175             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22176             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22177         }
22178     },
22179     
22180     // private
22181     initEditor : function(){
22182         //console.log("INIT EDITOR");
22183         this.assignDocWin();
22184         
22185         
22186         
22187         this.doc.designMode="on";
22188         this.doc.open();
22189         this.doc.write(this.getDocMarkup());
22190         this.doc.close();
22191         
22192         var dbody = (this.doc.body || this.doc.documentElement);
22193         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22194         // this copies styles from the containing element into thsi one..
22195         // not sure why we need all of this..
22196         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22197         
22198         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22199         //ss['background-attachment'] = 'fixed'; // w3c
22200         dbody.bgProperties = 'fixed'; // ie
22201         //Roo.DomHelper.applyStyles(dbody, ss);
22202         Roo.EventManager.on(this.doc, {
22203             //'mousedown': this.onEditorEvent,
22204             'mouseup': this.onEditorEvent,
22205             'dblclick': this.onEditorEvent,
22206             'click': this.onEditorEvent,
22207             'keyup': this.onEditorEvent,
22208             buffer:100,
22209             scope: this
22210         });
22211         if(Roo.isGecko){
22212             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22213         }
22214         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22215             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22216         }
22217         this.initialized = true;
22218
22219         this.owner.fireEvent('initialize', this);
22220         this.pushValue();
22221     },
22222
22223     // private
22224     onDestroy : function(){
22225         
22226         
22227         
22228         if(this.rendered){
22229             
22230             //for (var i =0; i < this.toolbars.length;i++) {
22231             //    // fixme - ask toolbars for heights?
22232             //    this.toolbars[i].onDestroy();
22233            // }
22234             
22235             //this.wrap.dom.innerHTML = '';
22236             //this.wrap.remove();
22237         }
22238     },
22239
22240     // private
22241     onFirstFocus : function(){
22242         
22243         this.assignDocWin();
22244         
22245         
22246         this.activated = true;
22247          
22248     
22249         if(Roo.isGecko){ // prevent silly gecko errors
22250             this.win.focus();
22251             var s = this.win.getSelection();
22252             if(!s.focusNode || s.focusNode.nodeType != 3){
22253                 var r = s.getRangeAt(0);
22254                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22255                 r.collapse(true);
22256                 this.deferFocus();
22257             }
22258             try{
22259                 this.execCmd('useCSS', true);
22260                 this.execCmd('styleWithCSS', false);
22261             }catch(e){}
22262         }
22263         this.owner.fireEvent('activate', this);
22264     },
22265
22266     // private
22267     adjustFont: function(btn){
22268         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22269         //if(Roo.isSafari){ // safari
22270         //    adjust *= 2;
22271        // }
22272         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22273         if(Roo.isSafari){ // safari
22274             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22275             v =  (v < 10) ? 10 : v;
22276             v =  (v > 48) ? 48 : v;
22277             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22278             
22279         }
22280         
22281         
22282         v = Math.max(1, v+adjust);
22283         
22284         this.execCmd('FontSize', v  );
22285     },
22286
22287     onEditorEvent : function(e)
22288     {
22289         this.owner.fireEvent('editorevent', this, e);
22290       //  this.updateToolbar();
22291         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22292     },
22293
22294     insertTag : function(tg)
22295     {
22296         // could be a bit smarter... -> wrap the current selected tRoo..
22297         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22298             
22299             range = this.createRange(this.getSelection());
22300             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22301             wrappingNode.appendChild(range.extractContents());
22302             range.insertNode(wrappingNode);
22303
22304             return;
22305             
22306             
22307             
22308         }
22309         this.execCmd("formatblock",   tg);
22310         
22311     },
22312     
22313     insertText : function(txt)
22314     {
22315         
22316         
22317         var range = this.createRange();
22318         range.deleteContents();
22319                //alert(Sender.getAttribute('label'));
22320                
22321         range.insertNode(this.doc.createTextNode(txt));
22322     } ,
22323     
22324      
22325
22326     /**
22327      * Executes a Midas editor command on the editor document and performs necessary focus and
22328      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22329      * @param {String} cmd The Midas command
22330      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22331      */
22332     relayCmd : function(cmd, value){
22333         this.win.focus();
22334         this.execCmd(cmd, value);
22335         this.owner.fireEvent('editorevent', this);
22336         //this.updateToolbar();
22337         this.owner.deferFocus();
22338     },
22339
22340     /**
22341      * Executes a Midas editor command directly on the editor document.
22342      * For visual commands, you should use {@link #relayCmd} instead.
22343      * <b>This should only be called after the editor is initialized.</b>
22344      * @param {String} cmd The Midas command
22345      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22346      */
22347     execCmd : function(cmd, value){
22348         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22349         this.syncValue();
22350     },
22351  
22352  
22353    
22354     /**
22355      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22356      * to insert tRoo.
22357      * @param {String} text | dom node.. 
22358      */
22359     insertAtCursor : function(text)
22360     {
22361         
22362         if(!this.activated){
22363             return;
22364         }
22365         /*
22366         if(Roo.isIE){
22367             this.win.focus();
22368             var r = this.doc.selection.createRange();
22369             if(r){
22370                 r.collapse(true);
22371                 r.pasteHTML(text);
22372                 this.syncValue();
22373                 this.deferFocus();
22374             
22375             }
22376             return;
22377         }
22378         */
22379         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22380             this.win.focus();
22381             
22382             
22383             // from jquery ui (MIT licenced)
22384             var range, node;
22385             var win = this.win;
22386             
22387             if (win.getSelection && win.getSelection().getRangeAt) {
22388                 range = win.getSelection().getRangeAt(0);
22389                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22390                 range.insertNode(node);
22391             } else if (win.document.selection && win.document.selection.createRange) {
22392                 // no firefox support
22393                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22394                 win.document.selection.createRange().pasteHTML(txt);
22395             } else {
22396                 // no firefox support
22397                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22398                 this.execCmd('InsertHTML', txt);
22399             } 
22400             
22401             this.syncValue();
22402             
22403             this.deferFocus();
22404         }
22405     },
22406  // private
22407     mozKeyPress : function(e){
22408         if(e.ctrlKey){
22409             var c = e.getCharCode(), cmd;
22410           
22411             if(c > 0){
22412                 c = String.fromCharCode(c).toLowerCase();
22413                 switch(c){
22414                     case 'b':
22415                         cmd = 'bold';
22416                         break;
22417                     case 'i':
22418                         cmd = 'italic';
22419                         break;
22420                     
22421                     case 'u':
22422                         cmd = 'underline';
22423                         break;
22424                     
22425                     case 'v':
22426                         this.cleanUpPaste.defer(100, this);
22427                         return;
22428                         
22429                 }
22430                 if(cmd){
22431                     this.win.focus();
22432                     this.execCmd(cmd);
22433                     this.deferFocus();
22434                     e.preventDefault();
22435                 }
22436                 
22437             }
22438         }
22439     },
22440
22441     // private
22442     fixKeys : function(){ // load time branching for fastest keydown performance
22443         if(Roo.isIE){
22444             return function(e){
22445                 var k = e.getKey(), r;
22446                 if(k == e.TAB){
22447                     e.stopEvent();
22448                     r = this.doc.selection.createRange();
22449                     if(r){
22450                         r.collapse(true);
22451                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22452                         this.deferFocus();
22453                     }
22454                     return;
22455                 }
22456                 
22457                 if(k == e.ENTER){
22458                     r = this.doc.selection.createRange();
22459                     if(r){
22460                         var target = r.parentElement();
22461                         if(!target || target.tagName.toLowerCase() != 'li'){
22462                             e.stopEvent();
22463                             r.pasteHTML('<br />');
22464                             r.collapse(false);
22465                             r.select();
22466                         }
22467                     }
22468                 }
22469                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22470                     this.cleanUpPaste.defer(100, this);
22471                     return;
22472                 }
22473                 
22474                 
22475             };
22476         }else if(Roo.isOpera){
22477             return function(e){
22478                 var k = e.getKey();
22479                 if(k == e.TAB){
22480                     e.stopEvent();
22481                     this.win.focus();
22482                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22483                     this.deferFocus();
22484                 }
22485                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22486                     this.cleanUpPaste.defer(100, this);
22487                     return;
22488                 }
22489                 
22490             };
22491         }else if(Roo.isSafari){
22492             return function(e){
22493                 var k = e.getKey();
22494                 
22495                 if(k == e.TAB){
22496                     e.stopEvent();
22497                     this.execCmd('InsertText','\t');
22498                     this.deferFocus();
22499                     return;
22500                 }
22501                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22502                     this.cleanUpPaste.defer(100, this);
22503                     return;
22504                 }
22505                 
22506              };
22507         }
22508     }(),
22509     
22510     getAllAncestors: function()
22511     {
22512         var p = this.getSelectedNode();
22513         var a = [];
22514         if (!p) {
22515             a.push(p); // push blank onto stack..
22516             p = this.getParentElement();
22517         }
22518         
22519         
22520         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22521             a.push(p);
22522             p = p.parentNode;
22523         }
22524         a.push(this.doc.body);
22525         return a;
22526     },
22527     lastSel : false,
22528     lastSelNode : false,
22529     
22530     
22531     getSelection : function() 
22532     {
22533         this.assignDocWin();
22534         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22535     },
22536     
22537     getSelectedNode: function() 
22538     {
22539         // this may only work on Gecko!!!
22540         
22541         // should we cache this!!!!
22542         
22543         
22544         
22545          
22546         var range = this.createRange(this.getSelection()).cloneRange();
22547         
22548         if (Roo.isIE) {
22549             var parent = range.parentElement();
22550             while (true) {
22551                 var testRange = range.duplicate();
22552                 testRange.moveToElementText(parent);
22553                 if (testRange.inRange(range)) {
22554                     break;
22555                 }
22556                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22557                     break;
22558                 }
22559                 parent = parent.parentElement;
22560             }
22561             return parent;
22562         }
22563         
22564         // is ancestor a text element.
22565         var ac =  range.commonAncestorContainer;
22566         if (ac.nodeType == 3) {
22567             ac = ac.parentNode;
22568         }
22569         
22570         var ar = ac.childNodes;
22571          
22572         var nodes = [];
22573         var other_nodes = [];
22574         var has_other_nodes = false;
22575         for (var i=0;i<ar.length;i++) {
22576             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22577                 continue;
22578             }
22579             // fullly contained node.
22580             
22581             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22582                 nodes.push(ar[i]);
22583                 continue;
22584             }
22585             
22586             // probably selected..
22587             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22588                 other_nodes.push(ar[i]);
22589                 continue;
22590             }
22591             // outer..
22592             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22593                 continue;
22594             }
22595             
22596             
22597             has_other_nodes = true;
22598         }
22599         if (!nodes.length && other_nodes.length) {
22600             nodes= other_nodes;
22601         }
22602         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22603             return false;
22604         }
22605         
22606         return nodes[0];
22607     },
22608     createRange: function(sel)
22609     {
22610         // this has strange effects when using with 
22611         // top toolbar - not sure if it's a great idea.
22612         //this.editor.contentWindow.focus();
22613         if (typeof sel != "undefined") {
22614             try {
22615                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22616             } catch(e) {
22617                 return this.doc.createRange();
22618             }
22619         } else {
22620             return this.doc.createRange();
22621         }
22622     },
22623     getParentElement: function()
22624     {
22625         
22626         this.assignDocWin();
22627         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22628         
22629         var range = this.createRange(sel);
22630          
22631         try {
22632             var p = range.commonAncestorContainer;
22633             while (p.nodeType == 3) { // text node
22634                 p = p.parentNode;
22635             }
22636             return p;
22637         } catch (e) {
22638             return null;
22639         }
22640     
22641     },
22642     /***
22643      *
22644      * Range intersection.. the hard stuff...
22645      *  '-1' = before
22646      *  '0' = hits..
22647      *  '1' = after.
22648      *         [ -- selected range --- ]
22649      *   [fail]                        [fail]
22650      *
22651      *    basically..
22652      *      if end is before start or  hits it. fail.
22653      *      if start is after end or hits it fail.
22654      *
22655      *   if either hits (but other is outside. - then it's not 
22656      *   
22657      *    
22658      **/
22659     
22660     
22661     // @see http://www.thismuchiknow.co.uk/?p=64.
22662     rangeIntersectsNode : function(range, node)
22663     {
22664         var nodeRange = node.ownerDocument.createRange();
22665         try {
22666             nodeRange.selectNode(node);
22667         } catch (e) {
22668             nodeRange.selectNodeContents(node);
22669         }
22670     
22671         var rangeStartRange = range.cloneRange();
22672         rangeStartRange.collapse(true);
22673     
22674         var rangeEndRange = range.cloneRange();
22675         rangeEndRange.collapse(false);
22676     
22677         var nodeStartRange = nodeRange.cloneRange();
22678         nodeStartRange.collapse(true);
22679     
22680         var nodeEndRange = nodeRange.cloneRange();
22681         nodeEndRange.collapse(false);
22682     
22683         return rangeStartRange.compareBoundaryPoints(
22684                  Range.START_TO_START, nodeEndRange) == -1 &&
22685                rangeEndRange.compareBoundaryPoints(
22686                  Range.START_TO_START, nodeStartRange) == 1;
22687         
22688          
22689     },
22690     rangeCompareNode : function(range, node)
22691     {
22692         var nodeRange = node.ownerDocument.createRange();
22693         try {
22694             nodeRange.selectNode(node);
22695         } catch (e) {
22696             nodeRange.selectNodeContents(node);
22697         }
22698         
22699         
22700         range.collapse(true);
22701     
22702         nodeRange.collapse(true);
22703      
22704         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22705         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22706          
22707         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22708         
22709         var nodeIsBefore   =  ss == 1;
22710         var nodeIsAfter    = ee == -1;
22711         
22712         if (nodeIsBefore && nodeIsAfter) {
22713             return 0; // outer
22714         }
22715         if (!nodeIsBefore && nodeIsAfter) {
22716             return 1; //right trailed.
22717         }
22718         
22719         if (nodeIsBefore && !nodeIsAfter) {
22720             return 2;  // left trailed.
22721         }
22722         // fully contined.
22723         return 3;
22724     },
22725
22726     // private? - in a new class?
22727     cleanUpPaste :  function()
22728     {
22729         // cleans up the whole document..
22730         Roo.log('cleanuppaste');
22731         
22732         this.cleanUpChildren(this.doc.body);
22733         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22734         if (clean != this.doc.body.innerHTML) {
22735             this.doc.body.innerHTML = clean;
22736         }
22737         
22738     },
22739     
22740     cleanWordChars : function(input) {// change the chars to hex code
22741         var he = Roo.HtmlEditorCore;
22742         
22743         var output = input;
22744         Roo.each(he.swapCodes, function(sw) { 
22745             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22746             
22747             output = output.replace(swapper, sw[1]);
22748         });
22749         
22750         return output;
22751     },
22752     
22753     
22754     cleanUpChildren : function (n)
22755     {
22756         if (!n.childNodes.length) {
22757             return;
22758         }
22759         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22760            this.cleanUpChild(n.childNodes[i]);
22761         }
22762     },
22763     
22764     
22765         
22766     
22767     cleanUpChild : function (node)
22768     {
22769         var ed = this;
22770         //console.log(node);
22771         if (node.nodeName == "#text") {
22772             // clean up silly Windows -- stuff?
22773             return; 
22774         }
22775         if (node.nodeName == "#comment") {
22776             node.parentNode.removeChild(node);
22777             // clean up silly Windows -- stuff?
22778             return; 
22779         }
22780         var lcname = node.tagName.toLowerCase();
22781         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22782         // whitelist of tags..
22783         
22784         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22785             // remove node.
22786             node.parentNode.removeChild(node);
22787             return;
22788             
22789         }
22790         
22791         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22792         
22793         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22794         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22795         
22796         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22797         //    remove_keep_children = true;
22798         //}
22799         
22800         if (remove_keep_children) {
22801             this.cleanUpChildren(node);
22802             // inserts everything just before this node...
22803             while (node.childNodes.length) {
22804                 var cn = node.childNodes[0];
22805                 node.removeChild(cn);
22806                 node.parentNode.insertBefore(cn, node);
22807             }
22808             node.parentNode.removeChild(node);
22809             return;
22810         }
22811         
22812         if (!node.attributes || !node.attributes.length) {
22813             this.cleanUpChildren(node);
22814             return;
22815         }
22816         
22817         function cleanAttr(n,v)
22818         {
22819             
22820             if (v.match(/^\./) || v.match(/^\//)) {
22821                 return;
22822             }
22823             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22824                 return;
22825             }
22826             if (v.match(/^#/)) {
22827                 return;
22828             }
22829 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22830             node.removeAttribute(n);
22831             
22832         }
22833         
22834         var cwhite = this.cwhite;
22835         var cblack = this.cblack;
22836             
22837         function cleanStyle(n,v)
22838         {
22839             if (v.match(/expression/)) { //XSS?? should we even bother..
22840                 node.removeAttribute(n);
22841                 return;
22842             }
22843             
22844             var parts = v.split(/;/);
22845             var clean = [];
22846             
22847             Roo.each(parts, function(p) {
22848                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22849                 if (!p.length) {
22850                     return true;
22851                 }
22852                 var l = p.split(':').shift().replace(/\s+/g,'');
22853                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22854                 
22855                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22856 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22857                     //node.removeAttribute(n);
22858                     return true;
22859                 }
22860                 //Roo.log()
22861                 // only allow 'c whitelisted system attributes'
22862                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22863 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22864                     //node.removeAttribute(n);
22865                     return true;
22866                 }
22867                 
22868                 
22869                  
22870                 
22871                 clean.push(p);
22872                 return true;
22873             });
22874             if (clean.length) { 
22875                 node.setAttribute(n, clean.join(';'));
22876             } else {
22877                 node.removeAttribute(n);
22878             }
22879             
22880         }
22881         
22882         
22883         for (var i = node.attributes.length-1; i > -1 ; i--) {
22884             var a = node.attributes[i];
22885             //console.log(a);
22886             
22887             if (a.name.toLowerCase().substr(0,2)=='on')  {
22888                 node.removeAttribute(a.name);
22889                 continue;
22890             }
22891             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22892                 node.removeAttribute(a.name);
22893                 continue;
22894             }
22895             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22896                 cleanAttr(a.name,a.value); // fixme..
22897                 continue;
22898             }
22899             if (a.name == 'style') {
22900                 cleanStyle(a.name,a.value);
22901                 continue;
22902             }
22903             /// clean up MS crap..
22904             // tecnically this should be a list of valid class'es..
22905             
22906             
22907             if (a.name == 'class') {
22908                 if (a.value.match(/^Mso/)) {
22909                     node.className = '';
22910                 }
22911                 
22912                 if (a.value.match(/^body$/)) {
22913                     node.className = '';
22914                 }
22915                 continue;
22916             }
22917             
22918             // style cleanup!?
22919             // class cleanup?
22920             
22921         }
22922         
22923         
22924         this.cleanUpChildren(node);
22925         
22926         
22927     },
22928     
22929     /**
22930      * Clean up MS wordisms...
22931      */
22932     cleanWord : function(node)
22933     {
22934         
22935         
22936         if (!node) {
22937             this.cleanWord(this.doc.body);
22938             return;
22939         }
22940         if (node.nodeName == "#text") {
22941             // clean up silly Windows -- stuff?
22942             return; 
22943         }
22944         if (node.nodeName == "#comment") {
22945             node.parentNode.removeChild(node);
22946             // clean up silly Windows -- stuff?
22947             return; 
22948         }
22949         
22950         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22951             node.parentNode.removeChild(node);
22952             return;
22953         }
22954         
22955         // remove - but keep children..
22956         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22957             while (node.childNodes.length) {
22958                 var cn = node.childNodes[0];
22959                 node.removeChild(cn);
22960                 node.parentNode.insertBefore(cn, node);
22961             }
22962             node.parentNode.removeChild(node);
22963             this.iterateChildren(node, this.cleanWord);
22964             return;
22965         }
22966         // clean styles
22967         if (node.className.length) {
22968             
22969             var cn = node.className.split(/\W+/);
22970             var cna = [];
22971             Roo.each(cn, function(cls) {
22972                 if (cls.match(/Mso[a-zA-Z]+/)) {
22973                     return;
22974                 }
22975                 cna.push(cls);
22976             });
22977             node.className = cna.length ? cna.join(' ') : '';
22978             if (!cna.length) {
22979                 node.removeAttribute("class");
22980             }
22981         }
22982         
22983         if (node.hasAttribute("lang")) {
22984             node.removeAttribute("lang");
22985         }
22986         
22987         if (node.hasAttribute("style")) {
22988             
22989             var styles = node.getAttribute("style").split(";");
22990             var nstyle = [];
22991             Roo.each(styles, function(s) {
22992                 if (!s.match(/:/)) {
22993                     return;
22994                 }
22995                 var kv = s.split(":");
22996                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22997                     return;
22998                 }
22999                 // what ever is left... we allow.
23000                 nstyle.push(s);
23001             });
23002             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23003             if (!nstyle.length) {
23004                 node.removeAttribute('style');
23005             }
23006         }
23007         this.iterateChildren(node, this.cleanWord);
23008         
23009         
23010         
23011     },
23012     /**
23013      * iterateChildren of a Node, calling fn each time, using this as the scole..
23014      * @param {DomNode} node node to iterate children of.
23015      * @param {Function} fn method of this class to call on each item.
23016      */
23017     iterateChildren : function(node, fn)
23018     {
23019         if (!node.childNodes.length) {
23020                 return;
23021         }
23022         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23023            fn.call(this, node.childNodes[i])
23024         }
23025     },
23026     
23027     
23028     /**
23029      * cleanTableWidths.
23030      *
23031      * Quite often pasting from word etc.. results in tables with column and widths.
23032      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23033      *
23034      */
23035     cleanTableWidths : function(node)
23036     {
23037          
23038          
23039         if (!node) {
23040             this.cleanTableWidths(this.doc.body);
23041             return;
23042         }
23043         
23044         // ignore list...
23045         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23046             return; 
23047         }
23048         Roo.log(node.tagName);
23049         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23050             this.iterateChildren(node, this.cleanTableWidths);
23051             return;
23052         }
23053         if (node.hasAttribute('width')) {
23054             node.removeAttribute('width');
23055         }
23056         
23057          
23058         if (node.hasAttribute("style")) {
23059             // pretty basic...
23060             
23061             var styles = node.getAttribute("style").split(";");
23062             var nstyle = [];
23063             Roo.each(styles, function(s) {
23064                 if (!s.match(/:/)) {
23065                     return;
23066                 }
23067                 var kv = s.split(":");
23068                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23069                     return;
23070                 }
23071                 // what ever is left... we allow.
23072                 nstyle.push(s);
23073             });
23074             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23075             if (!nstyle.length) {
23076                 node.removeAttribute('style');
23077             }
23078         }
23079         
23080         this.iterateChildren(node, this.cleanTableWidths);
23081         
23082         
23083     },
23084     
23085     
23086     
23087     
23088     domToHTML : function(currentElement, depth, nopadtext) {
23089         
23090         depth = depth || 0;
23091         nopadtext = nopadtext || false;
23092     
23093         if (!currentElement) {
23094             return this.domToHTML(this.doc.body);
23095         }
23096         
23097         //Roo.log(currentElement);
23098         var j;
23099         var allText = false;
23100         var nodeName = currentElement.nodeName;
23101         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23102         
23103         if  (nodeName == '#text') {
23104             
23105             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23106         }
23107         
23108         
23109         var ret = '';
23110         if (nodeName != 'BODY') {
23111              
23112             var i = 0;
23113             // Prints the node tagName, such as <A>, <IMG>, etc
23114             if (tagName) {
23115                 var attr = [];
23116                 for(i = 0; i < currentElement.attributes.length;i++) {
23117                     // quoting?
23118                     var aname = currentElement.attributes.item(i).name;
23119                     if (!currentElement.attributes.item(i).value.length) {
23120                         continue;
23121                     }
23122                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23123                 }
23124                 
23125                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23126             } 
23127             else {
23128                 
23129                 // eack
23130             }
23131         } else {
23132             tagName = false;
23133         }
23134         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23135             return ret;
23136         }
23137         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23138             nopadtext = true;
23139         }
23140         
23141         
23142         // Traverse the tree
23143         i = 0;
23144         var currentElementChild = currentElement.childNodes.item(i);
23145         var allText = true;
23146         var innerHTML  = '';
23147         lastnode = '';
23148         while (currentElementChild) {
23149             // Formatting code (indent the tree so it looks nice on the screen)
23150             var nopad = nopadtext;
23151             if (lastnode == 'SPAN') {
23152                 nopad  = true;
23153             }
23154             // text
23155             if  (currentElementChild.nodeName == '#text') {
23156                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23157                 toadd = nopadtext ? toadd : toadd.trim();
23158                 if (!nopad && toadd.length > 80) {
23159                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23160                 }
23161                 innerHTML  += toadd;
23162                 
23163                 i++;
23164                 currentElementChild = currentElement.childNodes.item(i);
23165                 lastNode = '';
23166                 continue;
23167             }
23168             allText = false;
23169             
23170             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23171                 
23172             // Recursively traverse the tree structure of the child node
23173             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23174             lastnode = currentElementChild.nodeName;
23175             i++;
23176             currentElementChild=currentElement.childNodes.item(i);
23177         }
23178         
23179         ret += innerHTML;
23180         
23181         if (!allText) {
23182                 // The remaining code is mostly for formatting the tree
23183             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23184         }
23185         
23186         
23187         if (tagName) {
23188             ret+= "</"+tagName+">";
23189         }
23190         return ret;
23191         
23192     },
23193         
23194     applyBlacklists : function()
23195     {
23196         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23197         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23198         
23199         this.white = [];
23200         this.black = [];
23201         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23202             if (b.indexOf(tag) > -1) {
23203                 return;
23204             }
23205             this.white.push(tag);
23206             
23207         }, this);
23208         
23209         Roo.each(w, function(tag) {
23210             if (b.indexOf(tag) > -1) {
23211                 return;
23212             }
23213             if (this.white.indexOf(tag) > -1) {
23214                 return;
23215             }
23216             this.white.push(tag);
23217             
23218         }, this);
23219         
23220         
23221         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23222             if (w.indexOf(tag) > -1) {
23223                 return;
23224             }
23225             this.black.push(tag);
23226             
23227         }, this);
23228         
23229         Roo.each(b, function(tag) {
23230             if (w.indexOf(tag) > -1) {
23231                 return;
23232             }
23233             if (this.black.indexOf(tag) > -1) {
23234                 return;
23235             }
23236             this.black.push(tag);
23237             
23238         }, this);
23239         
23240         
23241         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23242         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23243         
23244         this.cwhite = [];
23245         this.cblack = [];
23246         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23247             if (b.indexOf(tag) > -1) {
23248                 return;
23249             }
23250             this.cwhite.push(tag);
23251             
23252         }, this);
23253         
23254         Roo.each(w, function(tag) {
23255             if (b.indexOf(tag) > -1) {
23256                 return;
23257             }
23258             if (this.cwhite.indexOf(tag) > -1) {
23259                 return;
23260             }
23261             this.cwhite.push(tag);
23262             
23263         }, this);
23264         
23265         
23266         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23267             if (w.indexOf(tag) > -1) {
23268                 return;
23269             }
23270             this.cblack.push(tag);
23271             
23272         }, this);
23273         
23274         Roo.each(b, function(tag) {
23275             if (w.indexOf(tag) > -1) {
23276                 return;
23277             }
23278             if (this.cblack.indexOf(tag) > -1) {
23279                 return;
23280             }
23281             this.cblack.push(tag);
23282             
23283         }, this);
23284     },
23285     
23286     setStylesheets : function(stylesheets)
23287     {
23288         if(typeof(stylesheets) == 'string'){
23289             Roo.get(this.iframe.contentDocument.head).createChild({
23290                 tag : 'link',
23291                 rel : 'stylesheet',
23292                 type : 'text/css',
23293                 href : stylesheets
23294             });
23295             
23296             return;
23297         }
23298         var _this = this;
23299      
23300         Roo.each(stylesheets, function(s) {
23301             if(!s.length){
23302                 return;
23303             }
23304             
23305             Roo.get(_this.iframe.contentDocument.head).createChild({
23306                 tag : 'link',
23307                 rel : 'stylesheet',
23308                 type : 'text/css',
23309                 href : s
23310             });
23311         });
23312
23313         
23314     },
23315     
23316     removeStylesheets : function()
23317     {
23318         var _this = this;
23319         
23320         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23321             s.remove();
23322         });
23323     },
23324     
23325     setStyle : function(style)
23326     {
23327         Roo.get(this.iframe.contentDocument.head).createChild({
23328             tag : 'style',
23329             type : 'text/css',
23330             html : style
23331         });
23332
23333         return;
23334     }
23335     
23336     // hide stuff that is not compatible
23337     /**
23338      * @event blur
23339      * @hide
23340      */
23341     /**
23342      * @event change
23343      * @hide
23344      */
23345     /**
23346      * @event focus
23347      * @hide
23348      */
23349     /**
23350      * @event specialkey
23351      * @hide
23352      */
23353     /**
23354      * @cfg {String} fieldClass @hide
23355      */
23356     /**
23357      * @cfg {String} focusClass @hide
23358      */
23359     /**
23360      * @cfg {String} autoCreate @hide
23361      */
23362     /**
23363      * @cfg {String} inputType @hide
23364      */
23365     /**
23366      * @cfg {String} invalidClass @hide
23367      */
23368     /**
23369      * @cfg {String} invalidText @hide
23370      */
23371     /**
23372      * @cfg {String} msgFx @hide
23373      */
23374     /**
23375      * @cfg {String} validateOnBlur @hide
23376      */
23377 });
23378
23379 Roo.HtmlEditorCore.white = [
23380         'area', 'br', 'img', 'input', 'hr', 'wbr',
23381         
23382        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23383        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23384        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23385        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23386        'table',   'ul',         'xmp', 
23387        
23388        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23389       'thead',   'tr', 
23390      
23391       'dir', 'menu', 'ol', 'ul', 'dl',
23392        
23393       'embed',  'object'
23394 ];
23395
23396
23397 Roo.HtmlEditorCore.black = [
23398     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23399         'applet', // 
23400         'base',   'basefont', 'bgsound', 'blink',  'body', 
23401         'frame',  'frameset', 'head',    'html',   'ilayer', 
23402         'iframe', 'layer',  'link',     'meta',    'object',   
23403         'script', 'style' ,'title',  'xml' // clean later..
23404 ];
23405 Roo.HtmlEditorCore.clean = [
23406     'script', 'style', 'title', 'xml'
23407 ];
23408 Roo.HtmlEditorCore.remove = [
23409     'font'
23410 ];
23411 // attributes..
23412
23413 Roo.HtmlEditorCore.ablack = [
23414     'on'
23415 ];
23416     
23417 Roo.HtmlEditorCore.aclean = [ 
23418     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23419 ];
23420
23421 // protocols..
23422 Roo.HtmlEditorCore.pwhite= [
23423         'http',  'https',  'mailto'
23424 ];
23425
23426 // white listed style attributes.
23427 Roo.HtmlEditorCore.cwhite= [
23428       //  'text-align', /// default is to allow most things..
23429       
23430          
23431 //        'font-size'//??
23432 ];
23433
23434 // black listed style attributes.
23435 Roo.HtmlEditorCore.cblack= [
23436       //  'font-size' -- this can be set by the project 
23437 ];
23438
23439
23440 Roo.HtmlEditorCore.swapCodes   =[ 
23441     [    8211, "--" ], 
23442     [    8212, "--" ], 
23443     [    8216,  "'" ],  
23444     [    8217, "'" ],  
23445     [    8220, '"' ],  
23446     [    8221, '"' ],  
23447     [    8226, "*" ],  
23448     [    8230, "..." ]
23449 ]; 
23450
23451     /*
23452  * - LGPL
23453  *
23454  * HtmlEditor
23455  * 
23456  */
23457
23458 /**
23459  * @class Roo.bootstrap.HtmlEditor
23460  * @extends Roo.bootstrap.TextArea
23461  * Bootstrap HtmlEditor class
23462
23463  * @constructor
23464  * Create a new HtmlEditor
23465  * @param {Object} config The config object
23466  */
23467
23468 Roo.bootstrap.HtmlEditor = function(config){
23469     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23470     if (!this.toolbars) {
23471         this.toolbars = [];
23472     }
23473     
23474     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23475     this.addEvents({
23476             /**
23477              * @event initialize
23478              * Fires when the editor is fully initialized (including the iframe)
23479              * @param {HtmlEditor} this
23480              */
23481             initialize: true,
23482             /**
23483              * @event activate
23484              * Fires when the editor is first receives the focus. Any insertion must wait
23485              * until after this event.
23486              * @param {HtmlEditor} this
23487              */
23488             activate: true,
23489              /**
23490              * @event beforesync
23491              * Fires before the textarea is updated with content from the editor iframe. Return false
23492              * to cancel the sync.
23493              * @param {HtmlEditor} this
23494              * @param {String} html
23495              */
23496             beforesync: true,
23497              /**
23498              * @event beforepush
23499              * Fires before the iframe editor is updated with content from the textarea. Return false
23500              * to cancel the push.
23501              * @param {HtmlEditor} this
23502              * @param {String} html
23503              */
23504             beforepush: true,
23505              /**
23506              * @event sync
23507              * Fires when the textarea is updated with content from the editor iframe.
23508              * @param {HtmlEditor} this
23509              * @param {String} html
23510              */
23511             sync: true,
23512              /**
23513              * @event push
23514              * Fires when the iframe editor is updated with content from the textarea.
23515              * @param {HtmlEditor} this
23516              * @param {String} html
23517              */
23518             push: true,
23519              /**
23520              * @event editmodechange
23521              * Fires when the editor switches edit modes
23522              * @param {HtmlEditor} this
23523              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23524              */
23525             editmodechange: true,
23526             /**
23527              * @event editorevent
23528              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23529              * @param {HtmlEditor} this
23530              */
23531             editorevent: true,
23532             /**
23533              * @event firstfocus
23534              * Fires when on first focus - needed by toolbars..
23535              * @param {HtmlEditor} this
23536              */
23537             firstfocus: true,
23538             /**
23539              * @event autosave
23540              * Auto save the htmlEditor value as a file into Events
23541              * @param {HtmlEditor} this
23542              */
23543             autosave: true,
23544             /**
23545              * @event savedpreview
23546              * preview the saved version of htmlEditor
23547              * @param {HtmlEditor} this
23548              */
23549             savedpreview: true
23550         });
23551 };
23552
23553
23554 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23555     
23556     
23557       /**
23558      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23559      */
23560     toolbars : false,
23561     
23562      /**
23563     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23564     */
23565     btns : [],
23566    
23567      /**
23568      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23569      *                        Roo.resizable.
23570      */
23571     resizable : false,
23572      /**
23573      * @cfg {Number} height (in pixels)
23574      */   
23575     height: 300,
23576    /**
23577      * @cfg {Number} width (in pixels)
23578      */   
23579     width: false,
23580     
23581     /**
23582      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23583      * 
23584      */
23585     stylesheets: false,
23586     
23587     // id of frame..
23588     frameId: false,
23589     
23590     // private properties
23591     validationEvent : false,
23592     deferHeight: true,
23593     initialized : false,
23594     activated : false,
23595     
23596     onFocus : Roo.emptyFn,
23597     iframePad:3,
23598     hideMode:'offsets',
23599     
23600     tbContainer : false,
23601     
23602     bodyCls : '',
23603     
23604     toolbarContainer :function() {
23605         return this.wrap.select('.x-html-editor-tb',true).first();
23606     },
23607
23608     /**
23609      * Protected method that will not generally be called directly. It
23610      * is called when the editor creates its toolbar. Override this method if you need to
23611      * add custom toolbar buttons.
23612      * @param {HtmlEditor} editor
23613      */
23614     createToolbar : function(){
23615         Roo.log('renewing');
23616         Roo.log("create toolbars");
23617         
23618         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23619         this.toolbars[0].render(this.toolbarContainer());
23620         
23621         return;
23622         
23623 //        if (!editor.toolbars || !editor.toolbars.length) {
23624 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23625 //        }
23626 //        
23627 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23628 //            editor.toolbars[i] = Roo.factory(
23629 //                    typeof(editor.toolbars[i]) == 'string' ?
23630 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23631 //                Roo.bootstrap.HtmlEditor);
23632 //            editor.toolbars[i].init(editor);
23633 //        }
23634     },
23635
23636      
23637     // private
23638     onRender : function(ct, position)
23639     {
23640        // Roo.log("Call onRender: " + this.xtype);
23641         var _t = this;
23642         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23643       
23644         this.wrap = this.inputEl().wrap({
23645             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23646         });
23647         
23648         this.editorcore.onRender(ct, position);
23649          
23650         if (this.resizable) {
23651             this.resizeEl = new Roo.Resizable(this.wrap, {
23652                 pinned : true,
23653                 wrap: true,
23654                 dynamic : true,
23655                 minHeight : this.height,
23656                 height: this.height,
23657                 handles : this.resizable,
23658                 width: this.width,
23659                 listeners : {
23660                     resize : function(r, w, h) {
23661                         _t.onResize(w,h); // -something
23662                     }
23663                 }
23664             });
23665             
23666         }
23667         this.createToolbar(this);
23668        
23669         
23670         if(!this.width && this.resizable){
23671             this.setSize(this.wrap.getSize());
23672         }
23673         if (this.resizeEl) {
23674             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23675             // should trigger onReize..
23676         }
23677         
23678     },
23679
23680     // private
23681     onResize : function(w, h)
23682     {
23683         Roo.log('resize: ' +w + ',' + h );
23684         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23685         var ew = false;
23686         var eh = false;
23687         
23688         if(this.inputEl() ){
23689             if(typeof w == 'number'){
23690                 var aw = w - this.wrap.getFrameWidth('lr');
23691                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23692                 ew = aw;
23693             }
23694             if(typeof h == 'number'){
23695                  var tbh = -11;  // fixme it needs to tool bar size!
23696                 for (var i =0; i < this.toolbars.length;i++) {
23697                     // fixme - ask toolbars for heights?
23698                     tbh += this.toolbars[i].el.getHeight();
23699                     //if (this.toolbars[i].footer) {
23700                     //    tbh += this.toolbars[i].footer.el.getHeight();
23701                     //}
23702                 }
23703               
23704                 
23705                 
23706                 
23707                 
23708                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23709                 ah -= 5; // knock a few pixes off for look..
23710                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23711                 var eh = ah;
23712             }
23713         }
23714         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23715         this.editorcore.onResize(ew,eh);
23716         
23717     },
23718
23719     /**
23720      * Toggles the editor between standard and source edit mode.
23721      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23722      */
23723     toggleSourceEdit : function(sourceEditMode)
23724     {
23725         this.editorcore.toggleSourceEdit(sourceEditMode);
23726         
23727         if(this.editorcore.sourceEditMode){
23728             Roo.log('editor - showing textarea');
23729             
23730 //            Roo.log('in');
23731 //            Roo.log(this.syncValue());
23732             this.syncValue();
23733             this.inputEl().removeClass(['hide', 'x-hidden']);
23734             this.inputEl().dom.removeAttribute('tabIndex');
23735             this.inputEl().focus();
23736         }else{
23737             Roo.log('editor - hiding textarea');
23738 //            Roo.log('out')
23739 //            Roo.log(this.pushValue()); 
23740             this.pushValue();
23741             
23742             this.inputEl().addClass(['hide', 'x-hidden']);
23743             this.inputEl().dom.setAttribute('tabIndex', -1);
23744             //this.deferFocus();
23745         }
23746          
23747         if(this.resizable){
23748             this.setSize(this.wrap.getSize());
23749         }
23750         
23751         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23752     },
23753  
23754     // private (for BoxComponent)
23755     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23756
23757     // private (for BoxComponent)
23758     getResizeEl : function(){
23759         return this.wrap;
23760     },
23761
23762     // private (for BoxComponent)
23763     getPositionEl : function(){
23764         return this.wrap;
23765     },
23766
23767     // private
23768     initEvents : function(){
23769         this.originalValue = this.getValue();
23770     },
23771
23772 //    /**
23773 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23774 //     * @method
23775 //     */
23776 //    markInvalid : Roo.emptyFn,
23777 //    /**
23778 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23779 //     * @method
23780 //     */
23781 //    clearInvalid : Roo.emptyFn,
23782
23783     setValue : function(v){
23784         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23785         this.editorcore.pushValue();
23786     },
23787
23788      
23789     // private
23790     deferFocus : function(){
23791         this.focus.defer(10, this);
23792     },
23793
23794     // doc'ed in Field
23795     focus : function(){
23796         this.editorcore.focus();
23797         
23798     },
23799       
23800
23801     // private
23802     onDestroy : function(){
23803         
23804         
23805         
23806         if(this.rendered){
23807             
23808             for (var i =0; i < this.toolbars.length;i++) {
23809                 // fixme - ask toolbars for heights?
23810                 this.toolbars[i].onDestroy();
23811             }
23812             
23813             this.wrap.dom.innerHTML = '';
23814             this.wrap.remove();
23815         }
23816     },
23817
23818     // private
23819     onFirstFocus : function(){
23820         //Roo.log("onFirstFocus");
23821         this.editorcore.onFirstFocus();
23822          for (var i =0; i < this.toolbars.length;i++) {
23823             this.toolbars[i].onFirstFocus();
23824         }
23825         
23826     },
23827     
23828     // private
23829     syncValue : function()
23830     {   
23831         this.editorcore.syncValue();
23832     },
23833     
23834     pushValue : function()
23835     {   
23836         this.editorcore.pushValue();
23837     }
23838      
23839     
23840     // hide stuff that is not compatible
23841     /**
23842      * @event blur
23843      * @hide
23844      */
23845     /**
23846      * @event change
23847      * @hide
23848      */
23849     /**
23850      * @event focus
23851      * @hide
23852      */
23853     /**
23854      * @event specialkey
23855      * @hide
23856      */
23857     /**
23858      * @cfg {String} fieldClass @hide
23859      */
23860     /**
23861      * @cfg {String} focusClass @hide
23862      */
23863     /**
23864      * @cfg {String} autoCreate @hide
23865      */
23866     /**
23867      * @cfg {String} inputType @hide
23868      */
23869     /**
23870      * @cfg {String} invalidClass @hide
23871      */
23872     /**
23873      * @cfg {String} invalidText @hide
23874      */
23875     /**
23876      * @cfg {String} msgFx @hide
23877      */
23878     /**
23879      * @cfg {String} validateOnBlur @hide
23880      */
23881 });
23882  
23883     
23884    
23885    
23886    
23887       
23888 Roo.namespace('Roo.bootstrap.htmleditor');
23889 /**
23890  * @class Roo.bootstrap.HtmlEditorToolbar1
23891  * Basic Toolbar
23892  * 
23893  * Usage:
23894  *
23895  new Roo.bootstrap.HtmlEditor({
23896     ....
23897     toolbars : [
23898         new Roo.bootstrap.HtmlEditorToolbar1({
23899             disable : { fonts: 1 , format: 1, ..., ... , ...],
23900             btns : [ .... ]
23901         })
23902     }
23903      
23904  * 
23905  * @cfg {Object} disable List of elements to disable..
23906  * @cfg {Array} btns List of additional buttons.
23907  * 
23908  * 
23909  * NEEDS Extra CSS? 
23910  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23911  */
23912  
23913 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23914 {
23915     
23916     Roo.apply(this, config);
23917     
23918     // default disabled, based on 'good practice'..
23919     this.disable = this.disable || {};
23920     Roo.applyIf(this.disable, {
23921         fontSize : true,
23922         colors : true,
23923         specialElements : true
23924     });
23925     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23926     
23927     this.editor = config.editor;
23928     this.editorcore = config.editor.editorcore;
23929     
23930     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23931     
23932     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23933     // dont call parent... till later.
23934 }
23935 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23936      
23937     bar : true,
23938     
23939     editor : false,
23940     editorcore : false,
23941     
23942     
23943     formats : [
23944         "p" ,  
23945         "h1","h2","h3","h4","h5","h6", 
23946         "pre", "code", 
23947         "abbr", "acronym", "address", "cite", "samp", "var",
23948         'div','span'
23949     ],
23950     
23951     onRender : function(ct, position)
23952     {
23953        // Roo.log("Call onRender: " + this.xtype);
23954         
23955        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23956        Roo.log(this.el);
23957        this.el.dom.style.marginBottom = '0';
23958        var _this = this;
23959        var editorcore = this.editorcore;
23960        var editor= this.editor;
23961        
23962        var children = [];
23963        var btn = function(id,cmd , toggle, handler, html){
23964        
23965             var  event = toggle ? 'toggle' : 'click';
23966        
23967             var a = {
23968                 size : 'sm',
23969                 xtype: 'Button',
23970                 xns: Roo.bootstrap,
23971                 glyphicon : id,
23972                 cmd : id || cmd,
23973                 enableToggle:toggle !== false,
23974                 html : html || '',
23975                 pressed : toggle ? false : null,
23976                 listeners : {}
23977             };
23978             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23979                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23980             };
23981             children.push(a);
23982             return a;
23983        }
23984        
23985     //    var cb_box = function...
23986         
23987         var style = {
23988                 xtype: 'Button',
23989                 size : 'sm',
23990                 xns: Roo.bootstrap,
23991                 glyphicon : 'font',
23992                 //html : 'submit'
23993                 menu : {
23994                     xtype: 'Menu',
23995                     xns: Roo.bootstrap,
23996                     items:  []
23997                 }
23998         };
23999         Roo.each(this.formats, function(f) {
24000             style.menu.items.push({
24001                 xtype :'MenuItem',
24002                 xns: Roo.bootstrap,
24003                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24004                 tagname : f,
24005                 listeners : {
24006                     click : function()
24007                     {
24008                         editorcore.insertTag(this.tagname);
24009                         editor.focus();
24010                     }
24011                 }
24012                 
24013             });
24014         });
24015         children.push(style);   
24016         
24017         btn('bold',false,true);
24018         btn('italic',false,true);
24019         btn('align-left', 'justifyleft',true);
24020         btn('align-center', 'justifycenter',true);
24021         btn('align-right' , 'justifyright',true);
24022         btn('link', false, false, function(btn) {
24023             //Roo.log("create link?");
24024             var url = prompt(this.createLinkText, this.defaultLinkValue);
24025             if(url && url != 'http:/'+'/'){
24026                 this.editorcore.relayCmd('createlink', url);
24027             }
24028         }),
24029         btn('list','insertunorderedlist',true);
24030         btn('pencil', false,true, function(btn){
24031                 Roo.log(this);
24032                 this.toggleSourceEdit(btn.pressed);
24033         });
24034         
24035         if (this.editor.btns.length > 0) {
24036             for (var i = 0; i<this.editor.btns.length; i++) {
24037                 children.push(this.editor.btns[i]);
24038             }
24039         }
24040         
24041         /*
24042         var cog = {
24043                 xtype: 'Button',
24044                 size : 'sm',
24045                 xns: Roo.bootstrap,
24046                 glyphicon : 'cog',
24047                 //html : 'submit'
24048                 menu : {
24049                     xtype: 'Menu',
24050                     xns: Roo.bootstrap,
24051                     items:  []
24052                 }
24053         };
24054         
24055         cog.menu.items.push({
24056             xtype :'MenuItem',
24057             xns: Roo.bootstrap,
24058             html : Clean styles,
24059             tagname : f,
24060             listeners : {
24061                 click : function()
24062                 {
24063                     editorcore.insertTag(this.tagname);
24064                     editor.focus();
24065                 }
24066             }
24067             
24068         });
24069        */
24070         
24071          
24072        this.xtype = 'NavSimplebar';
24073         
24074         for(var i=0;i< children.length;i++) {
24075             
24076             this.buttons.add(this.addxtypeChild(children[i]));
24077             
24078         }
24079         
24080         editor.on('editorevent', this.updateToolbar, this);
24081     },
24082     onBtnClick : function(id)
24083     {
24084        this.editorcore.relayCmd(id);
24085        this.editorcore.focus();
24086     },
24087     
24088     /**
24089      * Protected method that will not generally be called directly. It triggers
24090      * a toolbar update by reading the markup state of the current selection in the editor.
24091      */
24092     updateToolbar: function(){
24093
24094         if(!this.editorcore.activated){
24095             this.editor.onFirstFocus(); // is this neeed?
24096             return;
24097         }
24098
24099         var btns = this.buttons; 
24100         var doc = this.editorcore.doc;
24101         btns.get('bold').setActive(doc.queryCommandState('bold'));
24102         btns.get('italic').setActive(doc.queryCommandState('italic'));
24103         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24104         
24105         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24106         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24107         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24108         
24109         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24110         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24111          /*
24112         
24113         var ans = this.editorcore.getAllAncestors();
24114         if (this.formatCombo) {
24115             
24116             
24117             var store = this.formatCombo.store;
24118             this.formatCombo.setValue("");
24119             for (var i =0; i < ans.length;i++) {
24120                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24121                     // select it..
24122                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24123                     break;
24124                 }
24125             }
24126         }
24127         
24128         
24129         
24130         // hides menus... - so this cant be on a menu...
24131         Roo.bootstrap.MenuMgr.hideAll();
24132         */
24133         Roo.bootstrap.MenuMgr.hideAll();
24134         //this.editorsyncValue();
24135     },
24136     onFirstFocus: function() {
24137         this.buttons.each(function(item){
24138            item.enable();
24139         });
24140     },
24141     toggleSourceEdit : function(sourceEditMode){
24142         
24143           
24144         if(sourceEditMode){
24145             Roo.log("disabling buttons");
24146            this.buttons.each( function(item){
24147                 if(item.cmd != 'pencil'){
24148                     item.disable();
24149                 }
24150             });
24151           
24152         }else{
24153             Roo.log("enabling buttons");
24154             if(this.editorcore.initialized){
24155                 this.buttons.each( function(item){
24156                     item.enable();
24157                 });
24158             }
24159             
24160         }
24161         Roo.log("calling toggole on editor");
24162         // tell the editor that it's been pressed..
24163         this.editor.toggleSourceEdit(sourceEditMode);
24164        
24165     }
24166 });
24167
24168
24169
24170
24171
24172 /**
24173  * @class Roo.bootstrap.Table.AbstractSelectionModel
24174  * @extends Roo.util.Observable
24175  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24176  * implemented by descendant classes.  This class should not be directly instantiated.
24177  * @constructor
24178  */
24179 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24180     this.locked = false;
24181     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24182 };
24183
24184
24185 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24186     /** @ignore Called by the grid automatically. Do not call directly. */
24187     init : function(grid){
24188         this.grid = grid;
24189         this.initEvents();
24190     },
24191
24192     /**
24193      * Locks the selections.
24194      */
24195     lock : function(){
24196         this.locked = true;
24197     },
24198
24199     /**
24200      * Unlocks the selections.
24201      */
24202     unlock : function(){
24203         this.locked = false;
24204     },
24205
24206     /**
24207      * Returns true if the selections are locked.
24208      * @return {Boolean}
24209      */
24210     isLocked : function(){
24211         return this.locked;
24212     }
24213 });
24214 /**
24215  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24216  * @class Roo.bootstrap.Table.RowSelectionModel
24217  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24218  * It supports multiple selections and keyboard selection/navigation. 
24219  * @constructor
24220  * @param {Object} config
24221  */
24222
24223 Roo.bootstrap.Table.RowSelectionModel = function(config){
24224     Roo.apply(this, config);
24225     this.selections = new Roo.util.MixedCollection(false, function(o){
24226         return o.id;
24227     });
24228
24229     this.last = false;
24230     this.lastActive = false;
24231
24232     this.addEvents({
24233         /**
24234              * @event selectionchange
24235              * Fires when the selection changes
24236              * @param {SelectionModel} this
24237              */
24238             "selectionchange" : true,
24239         /**
24240              * @event afterselectionchange
24241              * Fires after the selection changes (eg. by key press or clicking)
24242              * @param {SelectionModel} this
24243              */
24244             "afterselectionchange" : true,
24245         /**
24246              * @event beforerowselect
24247              * Fires when a row is selected being selected, return false to cancel.
24248              * @param {SelectionModel} this
24249              * @param {Number} rowIndex The selected index
24250              * @param {Boolean} keepExisting False if other selections will be cleared
24251              */
24252             "beforerowselect" : true,
24253         /**
24254              * @event rowselect
24255              * Fires when a row is selected.
24256              * @param {SelectionModel} this
24257              * @param {Number} rowIndex The selected index
24258              * @param {Roo.data.Record} r The record
24259              */
24260             "rowselect" : true,
24261         /**
24262              * @event rowdeselect
24263              * Fires when a row is deselected.
24264              * @param {SelectionModel} this
24265              * @param {Number} rowIndex The selected index
24266              */
24267         "rowdeselect" : true
24268     });
24269     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24270     this.locked = false;
24271  };
24272
24273 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24274     /**
24275      * @cfg {Boolean} singleSelect
24276      * True to allow selection of only one row at a time (defaults to false)
24277      */
24278     singleSelect : false,
24279
24280     // private
24281     initEvents : function()
24282     {
24283
24284         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24285         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24286         //}else{ // allow click to work like normal
24287          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24288         //}
24289         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24290         this.grid.on("rowclick", this.handleMouseDown, this);
24291         
24292         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24293             "up" : function(e){
24294                 if(!e.shiftKey){
24295                     this.selectPrevious(e.shiftKey);
24296                 }else if(this.last !== false && this.lastActive !== false){
24297                     var last = this.last;
24298                     this.selectRange(this.last,  this.lastActive-1);
24299                     this.grid.getView().focusRow(this.lastActive);
24300                     if(last !== false){
24301                         this.last = last;
24302                     }
24303                 }else{
24304                     this.selectFirstRow();
24305                 }
24306                 this.fireEvent("afterselectionchange", this);
24307             },
24308             "down" : function(e){
24309                 if(!e.shiftKey){
24310                     this.selectNext(e.shiftKey);
24311                 }else if(this.last !== false && this.lastActive !== false){
24312                     var last = this.last;
24313                     this.selectRange(this.last,  this.lastActive+1);
24314                     this.grid.getView().focusRow(this.lastActive);
24315                     if(last !== false){
24316                         this.last = last;
24317                     }
24318                 }else{
24319                     this.selectFirstRow();
24320                 }
24321                 this.fireEvent("afterselectionchange", this);
24322             },
24323             scope: this
24324         });
24325         this.grid.store.on('load', function(){
24326             this.selections.clear();
24327         },this);
24328         /*
24329         var view = this.grid.view;
24330         view.on("refresh", this.onRefresh, this);
24331         view.on("rowupdated", this.onRowUpdated, this);
24332         view.on("rowremoved", this.onRemove, this);
24333         */
24334     },
24335
24336     // private
24337     onRefresh : function()
24338     {
24339         var ds = this.grid.store, i, v = this.grid.view;
24340         var s = this.selections;
24341         s.each(function(r){
24342             if((i = ds.indexOfId(r.id)) != -1){
24343                 v.onRowSelect(i);
24344             }else{
24345                 s.remove(r);
24346             }
24347         });
24348     },
24349
24350     // private
24351     onRemove : function(v, index, r){
24352         this.selections.remove(r);
24353     },
24354
24355     // private
24356     onRowUpdated : function(v, index, r){
24357         if(this.isSelected(r)){
24358             v.onRowSelect(index);
24359         }
24360     },
24361
24362     /**
24363      * Select records.
24364      * @param {Array} records The records to select
24365      * @param {Boolean} keepExisting (optional) True to keep existing selections
24366      */
24367     selectRecords : function(records, keepExisting)
24368     {
24369         if(!keepExisting){
24370             this.clearSelections();
24371         }
24372             var ds = this.grid.store;
24373         for(var i = 0, len = records.length; i < len; i++){
24374             this.selectRow(ds.indexOf(records[i]), true);
24375         }
24376     },
24377
24378     /**
24379      * Gets the number of selected rows.
24380      * @return {Number}
24381      */
24382     getCount : function(){
24383         return this.selections.length;
24384     },
24385
24386     /**
24387      * Selects the first row in the grid.
24388      */
24389     selectFirstRow : function(){
24390         this.selectRow(0);
24391     },
24392
24393     /**
24394      * Select the last row.
24395      * @param {Boolean} keepExisting (optional) True to keep existing selections
24396      */
24397     selectLastRow : function(keepExisting){
24398         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24399         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24400     },
24401
24402     /**
24403      * Selects the row immediately following the last selected row.
24404      * @param {Boolean} keepExisting (optional) True to keep existing selections
24405      */
24406     selectNext : function(keepExisting)
24407     {
24408             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24409             this.selectRow(this.last+1, keepExisting);
24410             this.grid.getView().focusRow(this.last);
24411         }
24412     },
24413
24414     /**
24415      * Selects the row that precedes the last selected row.
24416      * @param {Boolean} keepExisting (optional) True to keep existing selections
24417      */
24418     selectPrevious : function(keepExisting){
24419         if(this.last){
24420             this.selectRow(this.last-1, keepExisting);
24421             this.grid.getView().focusRow(this.last);
24422         }
24423     },
24424
24425     /**
24426      * Returns the selected records
24427      * @return {Array} Array of selected records
24428      */
24429     getSelections : function(){
24430         return [].concat(this.selections.items);
24431     },
24432
24433     /**
24434      * Returns the first selected record.
24435      * @return {Record}
24436      */
24437     getSelected : function(){
24438         return this.selections.itemAt(0);
24439     },
24440
24441
24442     /**
24443      * Clears all selections.
24444      */
24445     clearSelections : function(fast)
24446     {
24447         if(this.locked) {
24448             return;
24449         }
24450         if(fast !== true){
24451                 var ds = this.grid.store;
24452             var s = this.selections;
24453             s.each(function(r){
24454                 this.deselectRow(ds.indexOfId(r.id));
24455             }, this);
24456             s.clear();
24457         }else{
24458             this.selections.clear();
24459         }
24460         this.last = false;
24461     },
24462
24463
24464     /**
24465      * Selects all rows.
24466      */
24467     selectAll : function(){
24468         if(this.locked) {
24469             return;
24470         }
24471         this.selections.clear();
24472         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24473             this.selectRow(i, true);
24474         }
24475     },
24476
24477     /**
24478      * Returns True if there is a selection.
24479      * @return {Boolean}
24480      */
24481     hasSelection : function(){
24482         return this.selections.length > 0;
24483     },
24484
24485     /**
24486      * Returns True if the specified row is selected.
24487      * @param {Number/Record} record The record or index of the record to check
24488      * @return {Boolean}
24489      */
24490     isSelected : function(index){
24491             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24492         return (r && this.selections.key(r.id) ? true : false);
24493     },
24494
24495     /**
24496      * Returns True if the specified record id is selected.
24497      * @param {String} id The id of record to check
24498      * @return {Boolean}
24499      */
24500     isIdSelected : function(id){
24501         return (this.selections.key(id) ? true : false);
24502     },
24503
24504
24505     // private
24506     handleMouseDBClick : function(e, t){
24507         
24508     },
24509     // private
24510     handleMouseDown : function(e, t)
24511     {
24512             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24513         if(this.isLocked() || rowIndex < 0 ){
24514             return;
24515         };
24516         if(e.shiftKey && this.last !== false){
24517             var last = this.last;
24518             this.selectRange(last, rowIndex, e.ctrlKey);
24519             this.last = last; // reset the last
24520             t.focus();
24521     
24522         }else{
24523             var isSelected = this.isSelected(rowIndex);
24524             //Roo.log("select row:" + rowIndex);
24525             if(isSelected){
24526                 this.deselectRow(rowIndex);
24527             } else {
24528                         this.selectRow(rowIndex, true);
24529             }
24530     
24531             /*
24532                 if(e.button !== 0 && isSelected){
24533                 alert('rowIndex 2: ' + rowIndex);
24534                     view.focusRow(rowIndex);
24535                 }else if(e.ctrlKey && isSelected){
24536                     this.deselectRow(rowIndex);
24537                 }else if(!isSelected){
24538                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24539                     view.focusRow(rowIndex);
24540                 }
24541             */
24542         }
24543         this.fireEvent("afterselectionchange", this);
24544     },
24545     // private
24546     handleDragableRowClick :  function(grid, rowIndex, e) 
24547     {
24548         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24549             this.selectRow(rowIndex, false);
24550             grid.view.focusRow(rowIndex);
24551              this.fireEvent("afterselectionchange", this);
24552         }
24553     },
24554     
24555     /**
24556      * Selects multiple rows.
24557      * @param {Array} rows Array of the indexes of the row to select
24558      * @param {Boolean} keepExisting (optional) True to keep existing selections
24559      */
24560     selectRows : function(rows, keepExisting){
24561         if(!keepExisting){
24562             this.clearSelections();
24563         }
24564         for(var i = 0, len = rows.length; i < len; i++){
24565             this.selectRow(rows[i], true);
24566         }
24567     },
24568
24569     /**
24570      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24571      * @param {Number} startRow The index of the first row in the range
24572      * @param {Number} endRow The index of the last row in the range
24573      * @param {Boolean} keepExisting (optional) True to retain existing selections
24574      */
24575     selectRange : function(startRow, endRow, keepExisting){
24576         if(this.locked) {
24577             return;
24578         }
24579         if(!keepExisting){
24580             this.clearSelections();
24581         }
24582         if(startRow <= endRow){
24583             for(var i = startRow; i <= endRow; i++){
24584                 this.selectRow(i, true);
24585             }
24586         }else{
24587             for(var i = startRow; i >= endRow; i--){
24588                 this.selectRow(i, true);
24589             }
24590         }
24591     },
24592
24593     /**
24594      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24595      * @param {Number} startRow The index of the first row in the range
24596      * @param {Number} endRow The index of the last row in the range
24597      */
24598     deselectRange : function(startRow, endRow, preventViewNotify){
24599         if(this.locked) {
24600             return;
24601         }
24602         for(var i = startRow; i <= endRow; i++){
24603             this.deselectRow(i, preventViewNotify);
24604         }
24605     },
24606
24607     /**
24608      * Selects a row.
24609      * @param {Number} row The index of the row to select
24610      * @param {Boolean} keepExisting (optional) True to keep existing selections
24611      */
24612     selectRow : function(index, keepExisting, preventViewNotify)
24613     {
24614             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24615             return;
24616         }
24617         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24618             if(!keepExisting || this.singleSelect){
24619                 this.clearSelections();
24620             }
24621             
24622             var r = this.grid.store.getAt(index);
24623             //console.log('selectRow - record id :' + r.id);
24624             
24625             this.selections.add(r);
24626             this.last = this.lastActive = index;
24627             if(!preventViewNotify){
24628                 var proxy = new Roo.Element(
24629                                 this.grid.getRowDom(index)
24630                 );
24631                 proxy.addClass('bg-info info');
24632             }
24633             this.fireEvent("rowselect", this, index, r);
24634             this.fireEvent("selectionchange", this);
24635         }
24636     },
24637
24638     /**
24639      * Deselects a row.
24640      * @param {Number} row The index of the row to deselect
24641      */
24642     deselectRow : function(index, preventViewNotify)
24643     {
24644         if(this.locked) {
24645             return;
24646         }
24647         if(this.last == index){
24648             this.last = false;
24649         }
24650         if(this.lastActive == index){
24651             this.lastActive = false;
24652         }
24653         
24654         var r = this.grid.store.getAt(index);
24655         if (!r) {
24656             return;
24657         }
24658         
24659         this.selections.remove(r);
24660         //.console.log('deselectRow - record id :' + r.id);
24661         if(!preventViewNotify){
24662         
24663             var proxy = new Roo.Element(
24664                 this.grid.getRowDom(index)
24665             );
24666             proxy.removeClass('bg-info info');
24667         }
24668         this.fireEvent("rowdeselect", this, index);
24669         this.fireEvent("selectionchange", this);
24670     },
24671
24672     // private
24673     restoreLast : function(){
24674         if(this._last){
24675             this.last = this._last;
24676         }
24677     },
24678
24679     // private
24680     acceptsNav : function(row, col, cm){
24681         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24682     },
24683
24684     // private
24685     onEditorKey : function(field, e){
24686         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24687         if(k == e.TAB){
24688             e.stopEvent();
24689             ed.completeEdit();
24690             if(e.shiftKey){
24691                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24692             }else{
24693                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24694             }
24695         }else if(k == e.ENTER && !e.ctrlKey){
24696             e.stopEvent();
24697             ed.completeEdit();
24698             if(e.shiftKey){
24699                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24700             }else{
24701                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24702             }
24703         }else if(k == e.ESC){
24704             ed.cancelEdit();
24705         }
24706         if(newCell){
24707             g.startEditing(newCell[0], newCell[1]);
24708         }
24709     }
24710 });
24711 /*
24712  * Based on:
24713  * Ext JS Library 1.1.1
24714  * Copyright(c) 2006-2007, Ext JS, LLC.
24715  *
24716  * Originally Released Under LGPL - original licence link has changed is not relivant.
24717  *
24718  * Fork - LGPL
24719  * <script type="text/javascript">
24720  */
24721  
24722 /**
24723  * @class Roo.bootstrap.PagingToolbar
24724  * @extends Roo.bootstrap.NavSimplebar
24725  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24726  * @constructor
24727  * Create a new PagingToolbar
24728  * @param {Object} config The config object
24729  * @param {Roo.data.Store} store
24730  */
24731 Roo.bootstrap.PagingToolbar = function(config)
24732 {
24733     // old args format still supported... - xtype is prefered..
24734         // created from xtype...
24735     
24736     this.ds = config.dataSource;
24737     
24738     if (config.store && !this.ds) {
24739         this.store= Roo.factory(config.store, Roo.data);
24740         this.ds = this.store;
24741         this.ds.xmodule = this.xmodule || false;
24742     }
24743     
24744     this.toolbarItems = [];
24745     if (config.items) {
24746         this.toolbarItems = config.items;
24747     }
24748     
24749     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24750     
24751     this.cursor = 0;
24752     
24753     if (this.ds) { 
24754         this.bind(this.ds);
24755     }
24756     
24757     if (Roo.bootstrap.version == 4) {
24758         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24759     } else {
24760         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24761     }
24762     
24763 };
24764
24765 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24766     /**
24767      * @cfg {Roo.data.Store} dataSource
24768      * The underlying data store providing the paged data
24769      */
24770     /**
24771      * @cfg {String/HTMLElement/Element} container
24772      * container The id or element that will contain the toolbar
24773      */
24774     /**
24775      * @cfg {Boolean} displayInfo
24776      * True to display the displayMsg (defaults to false)
24777      */
24778     /**
24779      * @cfg {Number} pageSize
24780      * The number of records to display per page (defaults to 20)
24781      */
24782     pageSize: 20,
24783     /**
24784      * @cfg {String} displayMsg
24785      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24786      */
24787     displayMsg : 'Displaying {0} - {1} of {2}',
24788     /**
24789      * @cfg {String} emptyMsg
24790      * The message to display when no records are found (defaults to "No data to display")
24791      */
24792     emptyMsg : 'No data to display',
24793     /**
24794      * Customizable piece of the default paging text (defaults to "Page")
24795      * @type String
24796      */
24797     beforePageText : "Page",
24798     /**
24799      * Customizable piece of the default paging text (defaults to "of %0")
24800      * @type String
24801      */
24802     afterPageText : "of {0}",
24803     /**
24804      * Customizable piece of the default paging text (defaults to "First Page")
24805      * @type String
24806      */
24807     firstText : "First Page",
24808     /**
24809      * Customizable piece of the default paging text (defaults to "Previous Page")
24810      * @type String
24811      */
24812     prevText : "Previous Page",
24813     /**
24814      * Customizable piece of the default paging text (defaults to "Next Page")
24815      * @type String
24816      */
24817     nextText : "Next Page",
24818     /**
24819      * Customizable piece of the default paging text (defaults to "Last Page")
24820      * @type String
24821      */
24822     lastText : "Last Page",
24823     /**
24824      * Customizable piece of the default paging text (defaults to "Refresh")
24825      * @type String
24826      */
24827     refreshText : "Refresh",
24828
24829     buttons : false,
24830     // private
24831     onRender : function(ct, position) 
24832     {
24833         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24834         this.navgroup.parentId = this.id;
24835         this.navgroup.onRender(this.el, null);
24836         // add the buttons to the navgroup
24837         
24838         if(this.displayInfo){
24839             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24840             this.displayEl = this.el.select('.x-paging-info', true).first();
24841 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24842 //            this.displayEl = navel.el.select('span',true).first();
24843         }
24844         
24845         var _this = this;
24846         
24847         if(this.buttons){
24848             Roo.each(_this.buttons, function(e){ // this might need to use render????
24849                Roo.factory(e).render(_this.el);
24850             });
24851         }
24852             
24853         Roo.each(_this.toolbarItems, function(e) {
24854             _this.navgroup.addItem(e);
24855         });
24856         
24857         
24858         this.first = this.navgroup.addItem({
24859             tooltip: this.firstText,
24860             cls: "prev btn-outline-secondary",
24861             html : ' <i class="fa fa-step-backward"></i>',
24862             disabled: true,
24863             preventDefault: true,
24864             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24865         });
24866         
24867         this.prev =  this.navgroup.addItem({
24868             tooltip: this.prevText,
24869             cls: "prev btn-outline-secondary",
24870             html : ' <i class="fa fa-backward"></i>',
24871             disabled: true,
24872             preventDefault: true,
24873             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24874         });
24875     //this.addSeparator();
24876         
24877         
24878         var field = this.navgroup.addItem( {
24879             tagtype : 'span',
24880             cls : 'x-paging-position  btn-outline-secondary',
24881              disabled: true,
24882             html : this.beforePageText  +
24883                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24884                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24885          } ); //?? escaped?
24886         
24887         this.field = field.el.select('input', true).first();
24888         this.field.on("keydown", this.onPagingKeydown, this);
24889         this.field.on("focus", function(){this.dom.select();});
24890     
24891     
24892         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24893         //this.field.setHeight(18);
24894         //this.addSeparator();
24895         this.next = this.navgroup.addItem({
24896             tooltip: this.nextText,
24897             cls: "next btn-outline-secondary",
24898             html : ' <i class="fa fa-forward"></i>',
24899             disabled: true,
24900             preventDefault: true,
24901             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24902         });
24903         this.last = this.navgroup.addItem({
24904             tooltip: this.lastText,
24905             html : ' <i class="fa fa-step-forward"></i>',
24906             cls: "next btn-outline-secondary",
24907             disabled: true,
24908             preventDefault: true,
24909             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24910         });
24911     //this.addSeparator();
24912         this.loading = this.navgroup.addItem({
24913             tooltip: this.refreshText,
24914             cls: "btn-outline-secondary",
24915             html : ' <i class="fa fa-refresh"></i>',
24916             preventDefault: true,
24917             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24918         });
24919         
24920     },
24921
24922     // private
24923     updateInfo : function(){
24924         if(this.displayEl){
24925             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24926             var msg = count == 0 ?
24927                 this.emptyMsg :
24928                 String.format(
24929                     this.displayMsg,
24930                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24931                 );
24932             this.displayEl.update(msg);
24933         }
24934     },
24935
24936     // private
24937     onLoad : function(ds, r, o)
24938     {
24939         this.cursor = o.params.start ? o.params.start : 0;
24940         
24941         var d = this.getPageData(),
24942             ap = d.activePage,
24943             ps = d.pages;
24944         
24945         
24946         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24947         this.field.dom.value = ap;
24948         this.first.setDisabled(ap == 1);
24949         this.prev.setDisabled(ap == 1);
24950         this.next.setDisabled(ap == ps);
24951         this.last.setDisabled(ap == ps);
24952         this.loading.enable();
24953         this.updateInfo();
24954     },
24955
24956     // private
24957     getPageData : function(){
24958         var total = this.ds.getTotalCount();
24959         return {
24960             total : total,
24961             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24962             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24963         };
24964     },
24965
24966     // private
24967     onLoadError : function(){
24968         this.loading.enable();
24969     },
24970
24971     // private
24972     onPagingKeydown : function(e){
24973         var k = e.getKey();
24974         var d = this.getPageData();
24975         if(k == e.RETURN){
24976             var v = this.field.dom.value, pageNum;
24977             if(!v || isNaN(pageNum = parseInt(v, 10))){
24978                 this.field.dom.value = d.activePage;
24979                 return;
24980             }
24981             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24982             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24983             e.stopEvent();
24984         }
24985         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))
24986         {
24987           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24988           this.field.dom.value = pageNum;
24989           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24990           e.stopEvent();
24991         }
24992         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24993         {
24994           var v = this.field.dom.value, pageNum; 
24995           var increment = (e.shiftKey) ? 10 : 1;
24996           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24997                 increment *= -1;
24998           }
24999           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25000             this.field.dom.value = d.activePage;
25001             return;
25002           }
25003           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25004           {
25005             this.field.dom.value = parseInt(v, 10) + increment;
25006             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25007             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25008           }
25009           e.stopEvent();
25010         }
25011     },
25012
25013     // private
25014     beforeLoad : function(){
25015         if(this.loading){
25016             this.loading.disable();
25017         }
25018     },
25019
25020     // private
25021     onClick : function(which){
25022         
25023         var ds = this.ds;
25024         if (!ds) {
25025             return;
25026         }
25027         
25028         switch(which){
25029             case "first":
25030                 ds.load({params:{start: 0, limit: this.pageSize}});
25031             break;
25032             case "prev":
25033                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25034             break;
25035             case "next":
25036                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25037             break;
25038             case "last":
25039                 var total = ds.getTotalCount();
25040                 var extra = total % this.pageSize;
25041                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25042                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25043             break;
25044             case "refresh":
25045                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25046             break;
25047         }
25048     },
25049
25050     /**
25051      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25052      * @param {Roo.data.Store} store The data store to unbind
25053      */
25054     unbind : function(ds){
25055         ds.un("beforeload", this.beforeLoad, this);
25056         ds.un("load", this.onLoad, this);
25057         ds.un("loadexception", this.onLoadError, this);
25058         ds.un("remove", this.updateInfo, this);
25059         ds.un("add", this.updateInfo, this);
25060         this.ds = undefined;
25061     },
25062
25063     /**
25064      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25065      * @param {Roo.data.Store} store The data store to bind
25066      */
25067     bind : function(ds){
25068         ds.on("beforeload", this.beforeLoad, this);
25069         ds.on("load", this.onLoad, this);
25070         ds.on("loadexception", this.onLoadError, this);
25071         ds.on("remove", this.updateInfo, this);
25072         ds.on("add", this.updateInfo, this);
25073         this.ds = ds;
25074     }
25075 });/*
25076  * - LGPL
25077  *
25078  * element
25079  * 
25080  */
25081
25082 /**
25083  * @class Roo.bootstrap.MessageBar
25084  * @extends Roo.bootstrap.Component
25085  * Bootstrap MessageBar class
25086  * @cfg {String} html contents of the MessageBar
25087  * @cfg {String} weight (info | success | warning | danger) default info
25088  * @cfg {String} beforeClass insert the bar before the given class
25089  * @cfg {Boolean} closable (true | false) default false
25090  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25091  * 
25092  * @constructor
25093  * Create a new Element
25094  * @param {Object} config The config object
25095  */
25096
25097 Roo.bootstrap.MessageBar = function(config){
25098     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25099 };
25100
25101 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25102     
25103     html: '',
25104     weight: 'info',
25105     closable: false,
25106     fixed: false,
25107     beforeClass: 'bootstrap-sticky-wrap',
25108     
25109     getAutoCreate : function(){
25110         
25111         var cfg = {
25112             tag: 'div',
25113             cls: 'alert alert-dismissable alert-' + this.weight,
25114             cn: [
25115                 {
25116                     tag: 'span',
25117                     cls: 'message',
25118                     html: this.html || ''
25119                 }
25120             ]
25121         };
25122         
25123         if(this.fixed){
25124             cfg.cls += ' alert-messages-fixed';
25125         }
25126         
25127         if(this.closable){
25128             cfg.cn.push({
25129                 tag: 'button',
25130                 cls: 'close',
25131                 html: 'x'
25132             });
25133         }
25134         
25135         return cfg;
25136     },
25137     
25138     onRender : function(ct, position)
25139     {
25140         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25141         
25142         if(!this.el){
25143             var cfg = Roo.apply({},  this.getAutoCreate());
25144             cfg.id = Roo.id();
25145             
25146             if (this.cls) {
25147                 cfg.cls += ' ' + this.cls;
25148             }
25149             if (this.style) {
25150                 cfg.style = this.style;
25151             }
25152             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25153             
25154             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25155         }
25156         
25157         this.el.select('>button.close').on('click', this.hide, this);
25158         
25159     },
25160     
25161     show : function()
25162     {
25163         if (!this.rendered) {
25164             this.render();
25165         }
25166         
25167         this.el.show();
25168         
25169         this.fireEvent('show', this);
25170         
25171     },
25172     
25173     hide : function()
25174     {
25175         if (!this.rendered) {
25176             this.render();
25177         }
25178         
25179         this.el.hide();
25180         
25181         this.fireEvent('hide', this);
25182     },
25183     
25184     update : function()
25185     {
25186 //        var e = this.el.dom.firstChild;
25187 //        
25188 //        if(this.closable){
25189 //            e = e.nextSibling;
25190 //        }
25191 //        
25192 //        e.data = this.html || '';
25193
25194         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25195     }
25196    
25197 });
25198
25199  
25200
25201      /*
25202  * - LGPL
25203  *
25204  * Graph
25205  * 
25206  */
25207
25208
25209 /**
25210  * @class Roo.bootstrap.Graph
25211  * @extends Roo.bootstrap.Component
25212  * Bootstrap Graph class
25213 > Prameters
25214  -sm {number} sm 4
25215  -md {number} md 5
25216  @cfg {String} graphtype  bar | vbar | pie
25217  @cfg {number} g_x coodinator | centre x (pie)
25218  @cfg {number} g_y coodinator | centre y (pie)
25219  @cfg {number} g_r radius (pie)
25220  @cfg {number} g_height height of the chart (respected by all elements in the set)
25221  @cfg {number} g_width width of the chart (respected by all elements in the set)
25222  @cfg {Object} title The title of the chart
25223     
25224  -{Array}  values
25225  -opts (object) options for the chart 
25226      o {
25227      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25228      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25229      o vgutter (number)
25230      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.
25231      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25232      o to
25233      o stretch (boolean)
25234      o }
25235  -opts (object) options for the pie
25236      o{
25237      o cut
25238      o startAngle (number)
25239      o endAngle (number)
25240      } 
25241  *
25242  * @constructor
25243  * Create a new Input
25244  * @param {Object} config The config object
25245  */
25246
25247 Roo.bootstrap.Graph = function(config){
25248     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25249     
25250     this.addEvents({
25251         // img events
25252         /**
25253          * @event click
25254          * The img click event for the img.
25255          * @param {Roo.EventObject} e
25256          */
25257         "click" : true
25258     });
25259 };
25260
25261 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25262     
25263     sm: 4,
25264     md: 5,
25265     graphtype: 'bar',
25266     g_height: 250,
25267     g_width: 400,
25268     g_x: 50,
25269     g_y: 50,
25270     g_r: 30,
25271     opts:{
25272         //g_colors: this.colors,
25273         g_type: 'soft',
25274         g_gutter: '20%'
25275
25276     },
25277     title : false,
25278
25279     getAutoCreate : function(){
25280         
25281         var cfg = {
25282             tag: 'div',
25283             html : null
25284         };
25285         
25286         
25287         return  cfg;
25288     },
25289
25290     onRender : function(ct,position){
25291         
25292         
25293         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25294         
25295         if (typeof(Raphael) == 'undefined') {
25296             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25297             return;
25298         }
25299         
25300         this.raphael = Raphael(this.el.dom);
25301         
25302                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25303                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25304                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25305                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25306                 /*
25307                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25308                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25309                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25310                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25311                 
25312                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25313                 r.barchart(330, 10, 300, 220, data1);
25314                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25315                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25316                 */
25317                 
25318                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25319                 // r.barchart(30, 30, 560, 250,  xdata, {
25320                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25321                 //     axis : "0 0 1 1",
25322                 //     axisxlabels :  xdata
25323                 //     //yvalues : cols,
25324                    
25325                 // });
25326 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25327 //        
25328 //        this.load(null,xdata,{
25329 //                axis : "0 0 1 1",
25330 //                axisxlabels :  xdata
25331 //                });
25332
25333     },
25334
25335     load : function(graphtype,xdata,opts)
25336     {
25337         this.raphael.clear();
25338         if(!graphtype) {
25339             graphtype = this.graphtype;
25340         }
25341         if(!opts){
25342             opts = this.opts;
25343         }
25344         var r = this.raphael,
25345             fin = function () {
25346                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25347             },
25348             fout = function () {
25349                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25350             },
25351             pfin = function() {
25352                 this.sector.stop();
25353                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25354
25355                 if (this.label) {
25356                     this.label[0].stop();
25357                     this.label[0].attr({ r: 7.5 });
25358                     this.label[1].attr({ "font-weight": 800 });
25359                 }
25360             },
25361             pfout = function() {
25362                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25363
25364                 if (this.label) {
25365                     this.label[0].animate({ r: 5 }, 500, "bounce");
25366                     this.label[1].attr({ "font-weight": 400 });
25367                 }
25368             };
25369
25370         switch(graphtype){
25371             case 'bar':
25372                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25373                 break;
25374             case 'hbar':
25375                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25376                 break;
25377             case 'pie':
25378 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25379 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25380 //            
25381                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25382                 
25383                 break;
25384
25385         }
25386         
25387         if(this.title){
25388             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25389         }
25390         
25391     },
25392     
25393     setTitle: function(o)
25394     {
25395         this.title = o;
25396     },
25397     
25398     initEvents: function() {
25399         
25400         if(!this.href){
25401             this.el.on('click', this.onClick, this);
25402         }
25403     },
25404     
25405     onClick : function(e)
25406     {
25407         Roo.log('img onclick');
25408         this.fireEvent('click', this, e);
25409     }
25410    
25411 });
25412
25413  
25414 /*
25415  * - LGPL
25416  *
25417  * numberBox
25418  * 
25419  */
25420 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25421
25422 /**
25423  * @class Roo.bootstrap.dash.NumberBox
25424  * @extends Roo.bootstrap.Component
25425  * Bootstrap NumberBox class
25426  * @cfg {String} headline Box headline
25427  * @cfg {String} content Box content
25428  * @cfg {String} icon Box icon
25429  * @cfg {String} footer Footer text
25430  * @cfg {String} fhref Footer href
25431  * 
25432  * @constructor
25433  * Create a new NumberBox
25434  * @param {Object} config The config object
25435  */
25436
25437
25438 Roo.bootstrap.dash.NumberBox = function(config){
25439     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25440     
25441 };
25442
25443 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25444     
25445     headline : '',
25446     content : '',
25447     icon : '',
25448     footer : '',
25449     fhref : '',
25450     ficon : '',
25451     
25452     getAutoCreate : function(){
25453         
25454         var cfg = {
25455             tag : 'div',
25456             cls : 'small-box ',
25457             cn : [
25458                 {
25459                     tag : 'div',
25460                     cls : 'inner',
25461                     cn :[
25462                         {
25463                             tag : 'h3',
25464                             cls : 'roo-headline',
25465                             html : this.headline
25466                         },
25467                         {
25468                             tag : 'p',
25469                             cls : 'roo-content',
25470                             html : this.content
25471                         }
25472                     ]
25473                 }
25474             ]
25475         };
25476         
25477         if(this.icon){
25478             cfg.cn.push({
25479                 tag : 'div',
25480                 cls : 'icon',
25481                 cn :[
25482                     {
25483                         tag : 'i',
25484                         cls : 'ion ' + this.icon
25485                     }
25486                 ]
25487             });
25488         }
25489         
25490         if(this.footer){
25491             var footer = {
25492                 tag : 'a',
25493                 cls : 'small-box-footer',
25494                 href : this.fhref || '#',
25495                 html : this.footer
25496             };
25497             
25498             cfg.cn.push(footer);
25499             
25500         }
25501         
25502         return  cfg;
25503     },
25504
25505     onRender : function(ct,position){
25506         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25507
25508
25509        
25510                 
25511     },
25512
25513     setHeadline: function (value)
25514     {
25515         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25516     },
25517     
25518     setFooter: function (value, href)
25519     {
25520         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25521         
25522         if(href){
25523             this.el.select('a.small-box-footer',true).first().attr('href', href);
25524         }
25525         
25526     },
25527
25528     setContent: function (value)
25529     {
25530         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25531     },
25532
25533     initEvents: function() 
25534     {   
25535         
25536     }
25537     
25538 });
25539
25540  
25541 /*
25542  * - LGPL
25543  *
25544  * TabBox
25545  * 
25546  */
25547 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25548
25549 /**
25550  * @class Roo.bootstrap.dash.TabBox
25551  * @extends Roo.bootstrap.Component
25552  * Bootstrap TabBox class
25553  * @cfg {String} title Title of the TabBox
25554  * @cfg {String} icon Icon of the TabBox
25555  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25556  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25557  * 
25558  * @constructor
25559  * Create a new TabBox
25560  * @param {Object} config The config object
25561  */
25562
25563
25564 Roo.bootstrap.dash.TabBox = function(config){
25565     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25566     this.addEvents({
25567         // raw events
25568         /**
25569          * @event addpane
25570          * When a pane is added
25571          * @param {Roo.bootstrap.dash.TabPane} pane
25572          */
25573         "addpane" : true,
25574         /**
25575          * @event activatepane
25576          * When a pane is activated
25577          * @param {Roo.bootstrap.dash.TabPane} pane
25578          */
25579         "activatepane" : true
25580         
25581          
25582     });
25583     
25584     this.panes = [];
25585 };
25586
25587 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25588
25589     title : '',
25590     icon : false,
25591     showtabs : true,
25592     tabScrollable : false,
25593     
25594     getChildContainer : function()
25595     {
25596         return this.el.select('.tab-content', true).first();
25597     },
25598     
25599     getAutoCreate : function(){
25600         
25601         var header = {
25602             tag: 'li',
25603             cls: 'pull-left header',
25604             html: this.title,
25605             cn : []
25606         };
25607         
25608         if(this.icon){
25609             header.cn.push({
25610                 tag: 'i',
25611                 cls: 'fa ' + this.icon
25612             });
25613         }
25614         
25615         var h = {
25616             tag: 'ul',
25617             cls: 'nav nav-tabs pull-right',
25618             cn: [
25619                 header
25620             ]
25621         };
25622         
25623         if(this.tabScrollable){
25624             h = {
25625                 tag: 'div',
25626                 cls: 'tab-header',
25627                 cn: [
25628                     {
25629                         tag: 'ul',
25630                         cls: 'nav nav-tabs pull-right',
25631                         cn: [
25632                             header
25633                         ]
25634                     }
25635                 ]
25636             };
25637         }
25638         
25639         var cfg = {
25640             tag: 'div',
25641             cls: 'nav-tabs-custom',
25642             cn: [
25643                 h,
25644                 {
25645                     tag: 'div',
25646                     cls: 'tab-content no-padding',
25647                     cn: []
25648                 }
25649             ]
25650         };
25651
25652         return  cfg;
25653     },
25654     initEvents : function()
25655     {
25656         //Roo.log('add add pane handler');
25657         this.on('addpane', this.onAddPane, this);
25658     },
25659      /**
25660      * Updates the box title
25661      * @param {String} html to set the title to.
25662      */
25663     setTitle : function(value)
25664     {
25665         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25666     },
25667     onAddPane : function(pane)
25668     {
25669         this.panes.push(pane);
25670         //Roo.log('addpane');
25671         //Roo.log(pane);
25672         // tabs are rendere left to right..
25673         if(!this.showtabs){
25674             return;
25675         }
25676         
25677         var ctr = this.el.select('.nav-tabs', true).first();
25678          
25679          
25680         var existing = ctr.select('.nav-tab',true);
25681         var qty = existing.getCount();;
25682         
25683         
25684         var tab = ctr.createChild({
25685             tag : 'li',
25686             cls : 'nav-tab' + (qty ? '' : ' active'),
25687             cn : [
25688                 {
25689                     tag : 'a',
25690                     href:'#',
25691                     html : pane.title
25692                 }
25693             ]
25694         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25695         pane.tab = tab;
25696         
25697         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25698         if (!qty) {
25699             pane.el.addClass('active');
25700         }
25701         
25702                 
25703     },
25704     onTabClick : function(ev,un,ob,pane)
25705     {
25706         //Roo.log('tab - prev default');
25707         ev.preventDefault();
25708         
25709         
25710         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25711         pane.tab.addClass('active');
25712         //Roo.log(pane.title);
25713         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25714         // technically we should have a deactivate event.. but maybe add later.
25715         // and it should not de-activate the selected tab...
25716         this.fireEvent('activatepane', pane);
25717         pane.el.addClass('active');
25718         pane.fireEvent('activate');
25719         
25720         
25721     },
25722     
25723     getActivePane : function()
25724     {
25725         var r = false;
25726         Roo.each(this.panes, function(p) {
25727             if(p.el.hasClass('active')){
25728                 r = p;
25729                 return false;
25730             }
25731             
25732             return;
25733         });
25734         
25735         return r;
25736     }
25737     
25738     
25739 });
25740
25741  
25742 /*
25743  * - LGPL
25744  *
25745  * Tab pane
25746  * 
25747  */
25748 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25749 /**
25750  * @class Roo.bootstrap.TabPane
25751  * @extends Roo.bootstrap.Component
25752  * Bootstrap TabPane class
25753  * @cfg {Boolean} active (false | true) Default false
25754  * @cfg {String} title title of panel
25755
25756  * 
25757  * @constructor
25758  * Create a new TabPane
25759  * @param {Object} config The config object
25760  */
25761
25762 Roo.bootstrap.dash.TabPane = function(config){
25763     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25764     
25765     this.addEvents({
25766         // raw events
25767         /**
25768          * @event activate
25769          * When a pane is activated
25770          * @param {Roo.bootstrap.dash.TabPane} pane
25771          */
25772         "activate" : true
25773          
25774     });
25775 };
25776
25777 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25778     
25779     active : false,
25780     title : '',
25781     
25782     // the tabBox that this is attached to.
25783     tab : false,
25784      
25785     getAutoCreate : function() 
25786     {
25787         var cfg = {
25788             tag: 'div',
25789             cls: 'tab-pane'
25790         };
25791         
25792         if(this.active){
25793             cfg.cls += ' active';
25794         }
25795         
25796         return cfg;
25797     },
25798     initEvents  : function()
25799     {
25800         //Roo.log('trigger add pane handler');
25801         this.parent().fireEvent('addpane', this)
25802     },
25803     
25804      /**
25805      * Updates the tab title 
25806      * @param {String} html to set the title to.
25807      */
25808     setTitle: function(str)
25809     {
25810         if (!this.tab) {
25811             return;
25812         }
25813         this.title = str;
25814         this.tab.select('a', true).first().dom.innerHTML = str;
25815         
25816     }
25817     
25818     
25819     
25820 });
25821
25822  
25823
25824
25825  /*
25826  * - LGPL
25827  *
25828  * menu
25829  * 
25830  */
25831 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25832
25833 /**
25834  * @class Roo.bootstrap.menu.Menu
25835  * @extends Roo.bootstrap.Component
25836  * Bootstrap Menu class - container for Menu
25837  * @cfg {String} html Text of the menu
25838  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25839  * @cfg {String} icon Font awesome icon
25840  * @cfg {String} pos Menu align to (top | bottom) default bottom
25841  * 
25842  * 
25843  * @constructor
25844  * Create a new Menu
25845  * @param {Object} config The config object
25846  */
25847
25848
25849 Roo.bootstrap.menu.Menu = function(config){
25850     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25851     
25852     this.addEvents({
25853         /**
25854          * @event beforeshow
25855          * Fires before this menu is displayed
25856          * @param {Roo.bootstrap.menu.Menu} this
25857          */
25858         beforeshow : true,
25859         /**
25860          * @event beforehide
25861          * Fires before this menu is hidden
25862          * @param {Roo.bootstrap.menu.Menu} this
25863          */
25864         beforehide : true,
25865         /**
25866          * @event show
25867          * Fires after this menu is displayed
25868          * @param {Roo.bootstrap.menu.Menu} this
25869          */
25870         show : true,
25871         /**
25872          * @event hide
25873          * Fires after this menu is hidden
25874          * @param {Roo.bootstrap.menu.Menu} this
25875          */
25876         hide : true,
25877         /**
25878          * @event click
25879          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25880          * @param {Roo.bootstrap.menu.Menu} this
25881          * @param {Roo.EventObject} e
25882          */
25883         click : true
25884     });
25885     
25886 };
25887
25888 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25889     
25890     submenu : false,
25891     html : '',
25892     weight : 'default',
25893     icon : false,
25894     pos : 'bottom',
25895     
25896     
25897     getChildContainer : function() {
25898         if(this.isSubMenu){
25899             return this.el;
25900         }
25901         
25902         return this.el.select('ul.dropdown-menu', true).first();  
25903     },
25904     
25905     getAutoCreate : function()
25906     {
25907         var text = [
25908             {
25909                 tag : 'span',
25910                 cls : 'roo-menu-text',
25911                 html : this.html
25912             }
25913         ];
25914         
25915         if(this.icon){
25916             text.unshift({
25917                 tag : 'i',
25918                 cls : 'fa ' + this.icon
25919             })
25920         }
25921         
25922         
25923         var cfg = {
25924             tag : 'div',
25925             cls : 'btn-group',
25926             cn : [
25927                 {
25928                     tag : 'button',
25929                     cls : 'dropdown-button btn btn-' + this.weight,
25930                     cn : text
25931                 },
25932                 {
25933                     tag : 'button',
25934                     cls : 'dropdown-toggle btn btn-' + this.weight,
25935                     cn : [
25936                         {
25937                             tag : 'span',
25938                             cls : 'caret'
25939                         }
25940                     ]
25941                 },
25942                 {
25943                     tag : 'ul',
25944                     cls : 'dropdown-menu'
25945                 }
25946             ]
25947             
25948         };
25949         
25950         if(this.pos == 'top'){
25951             cfg.cls += ' dropup';
25952         }
25953         
25954         if(this.isSubMenu){
25955             cfg = {
25956                 tag : 'ul',
25957                 cls : 'dropdown-menu'
25958             }
25959         }
25960         
25961         return cfg;
25962     },
25963     
25964     onRender : function(ct, position)
25965     {
25966         this.isSubMenu = ct.hasClass('dropdown-submenu');
25967         
25968         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25969     },
25970     
25971     initEvents : function() 
25972     {
25973         if(this.isSubMenu){
25974             return;
25975         }
25976         
25977         this.hidden = true;
25978         
25979         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25980         this.triggerEl.on('click', this.onTriggerPress, this);
25981         
25982         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25983         this.buttonEl.on('click', this.onClick, this);
25984         
25985     },
25986     
25987     list : function()
25988     {
25989         if(this.isSubMenu){
25990             return this.el;
25991         }
25992         
25993         return this.el.select('ul.dropdown-menu', true).first();
25994     },
25995     
25996     onClick : function(e)
25997     {
25998         this.fireEvent("click", this, e);
25999     },
26000     
26001     onTriggerPress  : function(e)
26002     {   
26003         if (this.isVisible()) {
26004             this.hide();
26005         } else {
26006             this.show();
26007         }
26008     },
26009     
26010     isVisible : function(){
26011         return !this.hidden;
26012     },
26013     
26014     show : function()
26015     {
26016         this.fireEvent("beforeshow", this);
26017         
26018         this.hidden = false;
26019         this.el.addClass('open');
26020         
26021         Roo.get(document).on("mouseup", this.onMouseUp, this);
26022         
26023         this.fireEvent("show", this);
26024         
26025         
26026     },
26027     
26028     hide : function()
26029     {
26030         this.fireEvent("beforehide", this);
26031         
26032         this.hidden = true;
26033         this.el.removeClass('open');
26034         
26035         Roo.get(document).un("mouseup", this.onMouseUp);
26036         
26037         this.fireEvent("hide", this);
26038     },
26039     
26040     onMouseUp : function()
26041     {
26042         this.hide();
26043     }
26044     
26045 });
26046
26047  
26048  /*
26049  * - LGPL
26050  *
26051  * menu item
26052  * 
26053  */
26054 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26055
26056 /**
26057  * @class Roo.bootstrap.menu.Item
26058  * @extends Roo.bootstrap.Component
26059  * Bootstrap MenuItem class
26060  * @cfg {Boolean} submenu (true | false) default false
26061  * @cfg {String} html text of the item
26062  * @cfg {String} href the link
26063  * @cfg {Boolean} disable (true | false) default false
26064  * @cfg {Boolean} preventDefault (true | false) default true
26065  * @cfg {String} icon Font awesome icon
26066  * @cfg {String} pos Submenu align to (left | right) default right 
26067  * 
26068  * 
26069  * @constructor
26070  * Create a new Item
26071  * @param {Object} config The config object
26072  */
26073
26074
26075 Roo.bootstrap.menu.Item = function(config){
26076     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26077     this.addEvents({
26078         /**
26079          * @event mouseover
26080          * Fires when the mouse is hovering over this menu
26081          * @param {Roo.bootstrap.menu.Item} this
26082          * @param {Roo.EventObject} e
26083          */
26084         mouseover : true,
26085         /**
26086          * @event mouseout
26087          * Fires when the mouse exits this menu
26088          * @param {Roo.bootstrap.menu.Item} this
26089          * @param {Roo.EventObject} e
26090          */
26091         mouseout : true,
26092         // raw events
26093         /**
26094          * @event click
26095          * The raw click event for the entire grid.
26096          * @param {Roo.EventObject} e
26097          */
26098         click : true
26099     });
26100 };
26101
26102 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26103     
26104     submenu : false,
26105     href : '',
26106     html : '',
26107     preventDefault: true,
26108     disable : false,
26109     icon : false,
26110     pos : 'right',
26111     
26112     getAutoCreate : function()
26113     {
26114         var text = [
26115             {
26116                 tag : 'span',
26117                 cls : 'roo-menu-item-text',
26118                 html : this.html
26119             }
26120         ];
26121         
26122         if(this.icon){
26123             text.unshift({
26124                 tag : 'i',
26125                 cls : 'fa ' + this.icon
26126             })
26127         }
26128         
26129         var cfg = {
26130             tag : 'li',
26131             cn : [
26132                 {
26133                     tag : 'a',
26134                     href : this.href || '#',
26135                     cn : text
26136                 }
26137             ]
26138         };
26139         
26140         if(this.disable){
26141             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26142         }
26143         
26144         if(this.submenu){
26145             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26146             
26147             if(this.pos == 'left'){
26148                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26149             }
26150         }
26151         
26152         return cfg;
26153     },
26154     
26155     initEvents : function() 
26156     {
26157         this.el.on('mouseover', this.onMouseOver, this);
26158         this.el.on('mouseout', this.onMouseOut, this);
26159         
26160         this.el.select('a', true).first().on('click', this.onClick, this);
26161         
26162     },
26163     
26164     onClick : function(e)
26165     {
26166         if(this.preventDefault){
26167             e.preventDefault();
26168         }
26169         
26170         this.fireEvent("click", this, e);
26171     },
26172     
26173     onMouseOver : function(e)
26174     {
26175         if(this.submenu && this.pos == 'left'){
26176             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26177         }
26178         
26179         this.fireEvent("mouseover", this, e);
26180     },
26181     
26182     onMouseOut : function(e)
26183     {
26184         this.fireEvent("mouseout", this, e);
26185     }
26186 });
26187
26188  
26189
26190  /*
26191  * - LGPL
26192  *
26193  * menu separator
26194  * 
26195  */
26196 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26197
26198 /**
26199  * @class Roo.bootstrap.menu.Separator
26200  * @extends Roo.bootstrap.Component
26201  * Bootstrap Separator class
26202  * 
26203  * @constructor
26204  * Create a new Separator
26205  * @param {Object} config The config object
26206  */
26207
26208
26209 Roo.bootstrap.menu.Separator = function(config){
26210     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26211 };
26212
26213 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26214     
26215     getAutoCreate : function(){
26216         var cfg = {
26217             tag : 'li',
26218             cls: 'divider'
26219         };
26220         
26221         return cfg;
26222     }
26223    
26224 });
26225
26226  
26227
26228  /*
26229  * - LGPL
26230  *
26231  * Tooltip
26232  * 
26233  */
26234
26235 /**
26236  * @class Roo.bootstrap.Tooltip
26237  * Bootstrap Tooltip class
26238  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26239  * to determine which dom element triggers the tooltip.
26240  * 
26241  * It needs to add support for additional attributes like tooltip-position
26242  * 
26243  * @constructor
26244  * Create a new Toolti
26245  * @param {Object} config The config object
26246  */
26247
26248 Roo.bootstrap.Tooltip = function(config){
26249     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26250     
26251     this.alignment = Roo.bootstrap.Tooltip.alignment;
26252     
26253     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26254         this.alignment = config.alignment;
26255     }
26256     
26257 };
26258
26259 Roo.apply(Roo.bootstrap.Tooltip, {
26260     /**
26261      * @function init initialize tooltip monitoring.
26262      * @static
26263      */
26264     currentEl : false,
26265     currentTip : false,
26266     currentRegion : false,
26267     
26268     //  init : delay?
26269     
26270     init : function()
26271     {
26272         Roo.get(document).on('mouseover', this.enter ,this);
26273         Roo.get(document).on('mouseout', this.leave, this);
26274          
26275         
26276         this.currentTip = new Roo.bootstrap.Tooltip();
26277     },
26278     
26279     enter : function(ev)
26280     {
26281         var dom = ev.getTarget();
26282         
26283         //Roo.log(['enter',dom]);
26284         var el = Roo.fly(dom);
26285         if (this.currentEl) {
26286             //Roo.log(dom);
26287             //Roo.log(this.currentEl);
26288             //Roo.log(this.currentEl.contains(dom));
26289             if (this.currentEl == el) {
26290                 return;
26291             }
26292             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26293                 return;
26294             }
26295
26296         }
26297         
26298         if (this.currentTip.el) {
26299             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26300         }    
26301         //Roo.log(ev);
26302         
26303         if(!el || el.dom == document){
26304             return;
26305         }
26306         
26307         var bindEl = el;
26308         
26309         // you can not look for children, as if el is the body.. then everythign is the child..
26310         if (!el.attr('tooltip')) { //
26311             if (!el.select("[tooltip]").elements.length) {
26312                 return;
26313             }
26314             // is the mouse over this child...?
26315             bindEl = el.select("[tooltip]").first();
26316             var xy = ev.getXY();
26317             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26318                 //Roo.log("not in region.");
26319                 return;
26320             }
26321             //Roo.log("child element over..");
26322             
26323         }
26324         this.currentEl = bindEl;
26325         this.currentTip.bind(bindEl);
26326         this.currentRegion = Roo.lib.Region.getRegion(dom);
26327         this.currentTip.enter();
26328         
26329     },
26330     leave : function(ev)
26331     {
26332         var dom = ev.getTarget();
26333         //Roo.log(['leave',dom]);
26334         if (!this.currentEl) {
26335             return;
26336         }
26337         
26338         
26339         if (dom != this.currentEl.dom) {
26340             return;
26341         }
26342         var xy = ev.getXY();
26343         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26344             return;
26345         }
26346         // only activate leave if mouse cursor is outside... bounding box..
26347         
26348         
26349         
26350         
26351         if (this.currentTip) {
26352             this.currentTip.leave();
26353         }
26354         //Roo.log('clear currentEl');
26355         this.currentEl = false;
26356         
26357         
26358     },
26359     alignment : {
26360         'left' : ['r-l', [-2,0], 'right'],
26361         'right' : ['l-r', [2,0], 'left'],
26362         'bottom' : ['t-b', [0,2], 'top'],
26363         'top' : [ 'b-t', [0,-2], 'bottom']
26364     }
26365     
26366 });
26367
26368
26369 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26370     
26371     
26372     bindEl : false,
26373     
26374     delay : null, // can be { show : 300 , hide: 500}
26375     
26376     timeout : null,
26377     
26378     hoverState : null, //???
26379     
26380     placement : 'bottom', 
26381     
26382     alignment : false,
26383     
26384     getAutoCreate : function(){
26385     
26386         var cfg = {
26387            cls : 'tooltip',
26388            role : 'tooltip',
26389            cn : [
26390                 {
26391                     cls : 'tooltip-arrow'
26392                 },
26393                 {
26394                     cls : 'tooltip-inner'
26395                 }
26396            ]
26397         };
26398         
26399         return cfg;
26400     },
26401     bind : function(el)
26402     {
26403         this.bindEl = el;
26404     },
26405       
26406     
26407     enter : function () {
26408        
26409         if (this.timeout != null) {
26410             clearTimeout(this.timeout);
26411         }
26412         
26413         this.hoverState = 'in';
26414          //Roo.log("enter - show");
26415         if (!this.delay || !this.delay.show) {
26416             this.show();
26417             return;
26418         }
26419         var _t = this;
26420         this.timeout = setTimeout(function () {
26421             if (_t.hoverState == 'in') {
26422                 _t.show();
26423             }
26424         }, this.delay.show);
26425     },
26426     leave : function()
26427     {
26428         clearTimeout(this.timeout);
26429     
26430         this.hoverState = 'out';
26431          if (!this.delay || !this.delay.hide) {
26432             this.hide();
26433             return;
26434         }
26435        
26436         var _t = this;
26437         this.timeout = setTimeout(function () {
26438             //Roo.log("leave - timeout");
26439             
26440             if (_t.hoverState == 'out') {
26441                 _t.hide();
26442                 Roo.bootstrap.Tooltip.currentEl = false;
26443             }
26444         }, delay);
26445     },
26446     
26447     show : function (msg)
26448     {
26449         if (!this.el) {
26450             this.render(document.body);
26451         }
26452         // set content.
26453         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26454         
26455         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26456         
26457         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26458         
26459         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26460         
26461         var placement = typeof this.placement == 'function' ?
26462             this.placement.call(this, this.el, on_el) :
26463             this.placement;
26464             
26465         var autoToken = /\s?auto?\s?/i;
26466         var autoPlace = autoToken.test(placement);
26467         if (autoPlace) {
26468             placement = placement.replace(autoToken, '') || 'top';
26469         }
26470         
26471         //this.el.detach()
26472         //this.el.setXY([0,0]);
26473         this.el.show();
26474         //this.el.dom.style.display='block';
26475         
26476         //this.el.appendTo(on_el);
26477         
26478         var p = this.getPosition();
26479         var box = this.el.getBox();
26480         
26481         if (autoPlace) {
26482             // fixme..
26483         }
26484         
26485         var align = this.alignment[placement];
26486         
26487         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26488         
26489         if(placement == 'top' || placement == 'bottom'){
26490             if(xy[0] < 0){
26491                 placement = 'right';
26492             }
26493             
26494             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26495                 placement = 'left';
26496             }
26497             
26498             var scroll = Roo.select('body', true).first().getScroll();
26499             
26500             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26501                 placement = 'top';
26502             }
26503             
26504             align = this.alignment[placement];
26505         }
26506         
26507         this.el.alignTo(this.bindEl, align[0],align[1]);
26508         //var arrow = this.el.select('.arrow',true).first();
26509         //arrow.set(align[2], 
26510         
26511         this.el.addClass(placement);
26512         
26513         this.el.addClass('in fade');
26514         
26515         this.hoverState = null;
26516         
26517         if (this.el.hasClass('fade')) {
26518             // fade it?
26519         }
26520         
26521     },
26522     hide : function()
26523     {
26524          
26525         if (!this.el) {
26526             return;
26527         }
26528         //this.el.setXY([0,0]);
26529         this.el.removeClass('in');
26530         //this.el.hide();
26531         
26532     }
26533     
26534 });
26535  
26536
26537  /*
26538  * - LGPL
26539  *
26540  * Location Picker
26541  * 
26542  */
26543
26544 /**
26545  * @class Roo.bootstrap.LocationPicker
26546  * @extends Roo.bootstrap.Component
26547  * Bootstrap LocationPicker class
26548  * @cfg {Number} latitude Position when init default 0
26549  * @cfg {Number} longitude Position when init default 0
26550  * @cfg {Number} zoom default 15
26551  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26552  * @cfg {Boolean} mapTypeControl default false
26553  * @cfg {Boolean} disableDoubleClickZoom default false
26554  * @cfg {Boolean} scrollwheel default true
26555  * @cfg {Boolean} streetViewControl default false
26556  * @cfg {Number} radius default 0
26557  * @cfg {String} locationName
26558  * @cfg {Boolean} draggable default true
26559  * @cfg {Boolean} enableAutocomplete default false
26560  * @cfg {Boolean} enableReverseGeocode default true
26561  * @cfg {String} markerTitle
26562  * 
26563  * @constructor
26564  * Create a new LocationPicker
26565  * @param {Object} config The config object
26566  */
26567
26568
26569 Roo.bootstrap.LocationPicker = function(config){
26570     
26571     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26572     
26573     this.addEvents({
26574         /**
26575          * @event initial
26576          * Fires when the picker initialized.
26577          * @param {Roo.bootstrap.LocationPicker} this
26578          * @param {Google Location} location
26579          */
26580         initial : true,
26581         /**
26582          * @event positionchanged
26583          * Fires when the picker position changed.
26584          * @param {Roo.bootstrap.LocationPicker} this
26585          * @param {Google Location} location
26586          */
26587         positionchanged : true,
26588         /**
26589          * @event resize
26590          * Fires when the map resize.
26591          * @param {Roo.bootstrap.LocationPicker} this
26592          */
26593         resize : true,
26594         /**
26595          * @event show
26596          * Fires when the map show.
26597          * @param {Roo.bootstrap.LocationPicker} this
26598          */
26599         show : true,
26600         /**
26601          * @event hide
26602          * Fires when the map hide.
26603          * @param {Roo.bootstrap.LocationPicker} this
26604          */
26605         hide : true,
26606         /**
26607          * @event mapClick
26608          * Fires when click the map.
26609          * @param {Roo.bootstrap.LocationPicker} this
26610          * @param {Map event} e
26611          */
26612         mapClick : true,
26613         /**
26614          * @event mapRightClick
26615          * Fires when right click the map.
26616          * @param {Roo.bootstrap.LocationPicker} this
26617          * @param {Map event} e
26618          */
26619         mapRightClick : true,
26620         /**
26621          * @event markerClick
26622          * Fires when click the marker.
26623          * @param {Roo.bootstrap.LocationPicker} this
26624          * @param {Map event} e
26625          */
26626         markerClick : true,
26627         /**
26628          * @event markerRightClick
26629          * Fires when right click the marker.
26630          * @param {Roo.bootstrap.LocationPicker} this
26631          * @param {Map event} e
26632          */
26633         markerRightClick : true,
26634         /**
26635          * @event OverlayViewDraw
26636          * Fires when OverlayView Draw
26637          * @param {Roo.bootstrap.LocationPicker} this
26638          */
26639         OverlayViewDraw : true,
26640         /**
26641          * @event OverlayViewOnAdd
26642          * Fires when OverlayView Draw
26643          * @param {Roo.bootstrap.LocationPicker} this
26644          */
26645         OverlayViewOnAdd : true,
26646         /**
26647          * @event OverlayViewOnRemove
26648          * Fires when OverlayView Draw
26649          * @param {Roo.bootstrap.LocationPicker} this
26650          */
26651         OverlayViewOnRemove : true,
26652         /**
26653          * @event OverlayViewShow
26654          * Fires when OverlayView Draw
26655          * @param {Roo.bootstrap.LocationPicker} this
26656          * @param {Pixel} cpx
26657          */
26658         OverlayViewShow : true,
26659         /**
26660          * @event OverlayViewHide
26661          * Fires when OverlayView Draw
26662          * @param {Roo.bootstrap.LocationPicker} this
26663          */
26664         OverlayViewHide : true,
26665         /**
26666          * @event loadexception
26667          * Fires when load google lib failed.
26668          * @param {Roo.bootstrap.LocationPicker} this
26669          */
26670         loadexception : true
26671     });
26672         
26673 };
26674
26675 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26676     
26677     gMapContext: false,
26678     
26679     latitude: 0,
26680     longitude: 0,
26681     zoom: 15,
26682     mapTypeId: false,
26683     mapTypeControl: false,
26684     disableDoubleClickZoom: false,
26685     scrollwheel: true,
26686     streetViewControl: false,
26687     radius: 0,
26688     locationName: '',
26689     draggable: true,
26690     enableAutocomplete: false,
26691     enableReverseGeocode: true,
26692     markerTitle: '',
26693     
26694     getAutoCreate: function()
26695     {
26696
26697         var cfg = {
26698             tag: 'div',
26699             cls: 'roo-location-picker'
26700         };
26701         
26702         return cfg
26703     },
26704     
26705     initEvents: function(ct, position)
26706     {       
26707         if(!this.el.getWidth() || this.isApplied()){
26708             return;
26709         }
26710         
26711         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26712         
26713         this.initial();
26714     },
26715     
26716     initial: function()
26717     {
26718         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26719             this.fireEvent('loadexception', this);
26720             return;
26721         }
26722         
26723         if(!this.mapTypeId){
26724             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26725         }
26726         
26727         this.gMapContext = this.GMapContext();
26728         
26729         this.initOverlayView();
26730         
26731         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26732         
26733         var _this = this;
26734                 
26735         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26736             _this.setPosition(_this.gMapContext.marker.position);
26737         });
26738         
26739         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26740             _this.fireEvent('mapClick', this, event);
26741             
26742         });
26743
26744         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26745             _this.fireEvent('mapRightClick', this, event);
26746             
26747         });
26748         
26749         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26750             _this.fireEvent('markerClick', this, event);
26751             
26752         });
26753
26754         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26755             _this.fireEvent('markerRightClick', this, event);
26756             
26757         });
26758         
26759         this.setPosition(this.gMapContext.location);
26760         
26761         this.fireEvent('initial', this, this.gMapContext.location);
26762     },
26763     
26764     initOverlayView: function()
26765     {
26766         var _this = this;
26767         
26768         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26769             
26770             draw: function()
26771             {
26772                 _this.fireEvent('OverlayViewDraw', _this);
26773             },
26774             
26775             onAdd: function()
26776             {
26777                 _this.fireEvent('OverlayViewOnAdd', _this);
26778             },
26779             
26780             onRemove: function()
26781             {
26782                 _this.fireEvent('OverlayViewOnRemove', _this);
26783             },
26784             
26785             show: function(cpx)
26786             {
26787                 _this.fireEvent('OverlayViewShow', _this, cpx);
26788             },
26789             
26790             hide: function()
26791             {
26792                 _this.fireEvent('OverlayViewHide', _this);
26793             }
26794             
26795         });
26796     },
26797     
26798     fromLatLngToContainerPixel: function(event)
26799     {
26800         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26801     },
26802     
26803     isApplied: function() 
26804     {
26805         return this.getGmapContext() == false ? false : true;
26806     },
26807     
26808     getGmapContext: function() 
26809     {
26810         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26811     },
26812     
26813     GMapContext: function() 
26814     {
26815         var position = new google.maps.LatLng(this.latitude, this.longitude);
26816         
26817         var _map = new google.maps.Map(this.el.dom, {
26818             center: position,
26819             zoom: this.zoom,
26820             mapTypeId: this.mapTypeId,
26821             mapTypeControl: this.mapTypeControl,
26822             disableDoubleClickZoom: this.disableDoubleClickZoom,
26823             scrollwheel: this.scrollwheel,
26824             streetViewControl: this.streetViewControl,
26825             locationName: this.locationName,
26826             draggable: this.draggable,
26827             enableAutocomplete: this.enableAutocomplete,
26828             enableReverseGeocode: this.enableReverseGeocode
26829         });
26830         
26831         var _marker = new google.maps.Marker({
26832             position: position,
26833             map: _map,
26834             title: this.markerTitle,
26835             draggable: this.draggable
26836         });
26837         
26838         return {
26839             map: _map,
26840             marker: _marker,
26841             circle: null,
26842             location: position,
26843             radius: this.radius,
26844             locationName: this.locationName,
26845             addressComponents: {
26846                 formatted_address: null,
26847                 addressLine1: null,
26848                 addressLine2: null,
26849                 streetName: null,
26850                 streetNumber: null,
26851                 city: null,
26852                 district: null,
26853                 state: null,
26854                 stateOrProvince: null
26855             },
26856             settings: this,
26857             domContainer: this.el.dom,
26858             geodecoder: new google.maps.Geocoder()
26859         };
26860     },
26861     
26862     drawCircle: function(center, radius, options) 
26863     {
26864         if (this.gMapContext.circle != null) {
26865             this.gMapContext.circle.setMap(null);
26866         }
26867         if (radius > 0) {
26868             radius *= 1;
26869             options = Roo.apply({}, options, {
26870                 strokeColor: "#0000FF",
26871                 strokeOpacity: .35,
26872                 strokeWeight: 2,
26873                 fillColor: "#0000FF",
26874                 fillOpacity: .2
26875             });
26876             
26877             options.map = this.gMapContext.map;
26878             options.radius = radius;
26879             options.center = center;
26880             this.gMapContext.circle = new google.maps.Circle(options);
26881             return this.gMapContext.circle;
26882         }
26883         
26884         return null;
26885     },
26886     
26887     setPosition: function(location) 
26888     {
26889         this.gMapContext.location = location;
26890         this.gMapContext.marker.setPosition(location);
26891         this.gMapContext.map.panTo(location);
26892         this.drawCircle(location, this.gMapContext.radius, {});
26893         
26894         var _this = this;
26895         
26896         if (this.gMapContext.settings.enableReverseGeocode) {
26897             this.gMapContext.geodecoder.geocode({
26898                 latLng: this.gMapContext.location
26899             }, function(results, status) {
26900                 
26901                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26902                     _this.gMapContext.locationName = results[0].formatted_address;
26903                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26904                     
26905                     _this.fireEvent('positionchanged', this, location);
26906                 }
26907             });
26908             
26909             return;
26910         }
26911         
26912         this.fireEvent('positionchanged', this, location);
26913     },
26914     
26915     resize: function()
26916     {
26917         google.maps.event.trigger(this.gMapContext.map, "resize");
26918         
26919         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26920         
26921         this.fireEvent('resize', this);
26922     },
26923     
26924     setPositionByLatLng: function(latitude, longitude)
26925     {
26926         this.setPosition(new google.maps.LatLng(latitude, longitude));
26927     },
26928     
26929     getCurrentPosition: function() 
26930     {
26931         return {
26932             latitude: this.gMapContext.location.lat(),
26933             longitude: this.gMapContext.location.lng()
26934         };
26935     },
26936     
26937     getAddressName: function() 
26938     {
26939         return this.gMapContext.locationName;
26940     },
26941     
26942     getAddressComponents: function() 
26943     {
26944         return this.gMapContext.addressComponents;
26945     },
26946     
26947     address_component_from_google_geocode: function(address_components) 
26948     {
26949         var result = {};
26950         
26951         for (var i = 0; i < address_components.length; i++) {
26952             var component = address_components[i];
26953             if (component.types.indexOf("postal_code") >= 0) {
26954                 result.postalCode = component.short_name;
26955             } else if (component.types.indexOf("street_number") >= 0) {
26956                 result.streetNumber = component.short_name;
26957             } else if (component.types.indexOf("route") >= 0) {
26958                 result.streetName = component.short_name;
26959             } else if (component.types.indexOf("neighborhood") >= 0) {
26960                 result.city = component.short_name;
26961             } else if (component.types.indexOf("locality") >= 0) {
26962                 result.city = component.short_name;
26963             } else if (component.types.indexOf("sublocality") >= 0) {
26964                 result.district = component.short_name;
26965             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26966                 result.stateOrProvince = component.short_name;
26967             } else if (component.types.indexOf("country") >= 0) {
26968                 result.country = component.short_name;
26969             }
26970         }
26971         
26972         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26973         result.addressLine2 = "";
26974         return result;
26975     },
26976     
26977     setZoomLevel: function(zoom)
26978     {
26979         this.gMapContext.map.setZoom(zoom);
26980     },
26981     
26982     show: function()
26983     {
26984         if(!this.el){
26985             return;
26986         }
26987         
26988         this.el.show();
26989         
26990         this.resize();
26991         
26992         this.fireEvent('show', this);
26993     },
26994     
26995     hide: function()
26996     {
26997         if(!this.el){
26998             return;
26999         }
27000         
27001         this.el.hide();
27002         
27003         this.fireEvent('hide', this);
27004     }
27005     
27006 });
27007
27008 Roo.apply(Roo.bootstrap.LocationPicker, {
27009     
27010     OverlayView : function(map, options)
27011     {
27012         options = options || {};
27013         
27014         this.setMap(map);
27015     }
27016     
27017     
27018 });/*
27019  * - LGPL
27020  *
27021  * Alert
27022  * 
27023  */
27024
27025 /**
27026  * @class Roo.bootstrap.Alert
27027  * @extends Roo.bootstrap.Component
27028  * Bootstrap Alert class
27029  * @cfg {String} title The title of alert
27030  * @cfg {String} html The content of alert
27031  * @cfg {String} weight (  success | info | warning | danger )
27032  * @cfg {String} faicon font-awesomeicon
27033  * 
27034  * @constructor
27035  * Create a new alert
27036  * @param {Object} config The config object
27037  */
27038
27039
27040 Roo.bootstrap.Alert = function(config){
27041     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27042     
27043 };
27044
27045 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27046     
27047     title: '',
27048     html: '',
27049     weight: false,
27050     faicon: false,
27051     
27052     getAutoCreate : function()
27053     {
27054         
27055         var cfg = {
27056             tag : 'div',
27057             cls : 'alert',
27058             cn : [
27059                 {
27060                     tag : 'i',
27061                     cls : 'roo-alert-icon'
27062                     
27063                 },
27064                 {
27065                     tag : 'b',
27066                     cls : 'roo-alert-title',
27067                     html : this.title
27068                 },
27069                 {
27070                     tag : 'span',
27071                     cls : 'roo-alert-text',
27072                     html : this.html
27073                 }
27074             ]
27075         };
27076         
27077         if(this.faicon){
27078             cfg.cn[0].cls += ' fa ' + this.faicon;
27079         }
27080         
27081         if(this.weight){
27082             cfg.cls += ' alert-' + this.weight;
27083         }
27084         
27085         return cfg;
27086     },
27087     
27088     initEvents: function() 
27089     {
27090         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27091     },
27092     
27093     setTitle : function(str)
27094     {
27095         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27096     },
27097     
27098     setText : function(str)
27099     {
27100         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27101     },
27102     
27103     setWeight : function(weight)
27104     {
27105         if(this.weight){
27106             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27107         }
27108         
27109         this.weight = weight;
27110         
27111         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27112     },
27113     
27114     setIcon : function(icon)
27115     {
27116         if(this.faicon){
27117             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27118         }
27119         
27120         this.faicon = icon;
27121         
27122         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27123     },
27124     
27125     hide: function() 
27126     {
27127         this.el.hide();   
27128     },
27129     
27130     show: function() 
27131     {  
27132         this.el.show();   
27133     }
27134     
27135 });
27136
27137  
27138 /*
27139 * Licence: LGPL
27140 */
27141
27142 /**
27143  * @class Roo.bootstrap.UploadCropbox
27144  * @extends Roo.bootstrap.Component
27145  * Bootstrap UploadCropbox class
27146  * @cfg {String} emptyText show when image has been loaded
27147  * @cfg {String} rotateNotify show when image too small to rotate
27148  * @cfg {Number} errorTimeout default 3000
27149  * @cfg {Number} minWidth default 300
27150  * @cfg {Number} minHeight default 300
27151  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27152  * @cfg {Boolean} isDocument (true|false) default false
27153  * @cfg {String} url action url
27154  * @cfg {String} paramName default 'imageUpload'
27155  * @cfg {String} method default POST
27156  * @cfg {Boolean} loadMask (true|false) default true
27157  * @cfg {Boolean} loadingText default 'Loading...'
27158  * 
27159  * @constructor
27160  * Create a new UploadCropbox
27161  * @param {Object} config The config object
27162  */
27163
27164 Roo.bootstrap.UploadCropbox = function(config){
27165     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27166     
27167     this.addEvents({
27168         /**
27169          * @event beforeselectfile
27170          * Fire before select file
27171          * @param {Roo.bootstrap.UploadCropbox} this
27172          */
27173         "beforeselectfile" : true,
27174         /**
27175          * @event initial
27176          * Fire after initEvent
27177          * @param {Roo.bootstrap.UploadCropbox} this
27178          */
27179         "initial" : true,
27180         /**
27181          * @event crop
27182          * Fire after initEvent
27183          * @param {Roo.bootstrap.UploadCropbox} this
27184          * @param {String} data
27185          */
27186         "crop" : true,
27187         /**
27188          * @event prepare
27189          * Fire when preparing the file data
27190          * @param {Roo.bootstrap.UploadCropbox} this
27191          * @param {Object} file
27192          */
27193         "prepare" : true,
27194         /**
27195          * @event exception
27196          * Fire when get exception
27197          * @param {Roo.bootstrap.UploadCropbox} this
27198          * @param {XMLHttpRequest} xhr
27199          */
27200         "exception" : true,
27201         /**
27202          * @event beforeloadcanvas
27203          * Fire before load the canvas
27204          * @param {Roo.bootstrap.UploadCropbox} this
27205          * @param {String} src
27206          */
27207         "beforeloadcanvas" : true,
27208         /**
27209          * @event trash
27210          * Fire when trash image
27211          * @param {Roo.bootstrap.UploadCropbox} this
27212          */
27213         "trash" : true,
27214         /**
27215          * @event download
27216          * Fire when download the image
27217          * @param {Roo.bootstrap.UploadCropbox} this
27218          */
27219         "download" : true,
27220         /**
27221          * @event footerbuttonclick
27222          * Fire when footerbuttonclick
27223          * @param {Roo.bootstrap.UploadCropbox} this
27224          * @param {String} type
27225          */
27226         "footerbuttonclick" : true,
27227         /**
27228          * @event resize
27229          * Fire when resize
27230          * @param {Roo.bootstrap.UploadCropbox} this
27231          */
27232         "resize" : true,
27233         /**
27234          * @event rotate
27235          * Fire when rotate the image
27236          * @param {Roo.bootstrap.UploadCropbox} this
27237          * @param {String} pos
27238          */
27239         "rotate" : true,
27240         /**
27241          * @event inspect
27242          * Fire when inspect the file
27243          * @param {Roo.bootstrap.UploadCropbox} this
27244          * @param {Object} file
27245          */
27246         "inspect" : true,
27247         /**
27248          * @event upload
27249          * Fire when xhr upload the file
27250          * @param {Roo.bootstrap.UploadCropbox} this
27251          * @param {Object} data
27252          */
27253         "upload" : true,
27254         /**
27255          * @event arrange
27256          * Fire when arrange the file data
27257          * @param {Roo.bootstrap.UploadCropbox} this
27258          * @param {Object} formData
27259          */
27260         "arrange" : true
27261     });
27262     
27263     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27264 };
27265
27266 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27267     
27268     emptyText : 'Click to upload image',
27269     rotateNotify : 'Image is too small to rotate',
27270     errorTimeout : 3000,
27271     scale : 0,
27272     baseScale : 1,
27273     rotate : 0,
27274     dragable : false,
27275     pinching : false,
27276     mouseX : 0,
27277     mouseY : 0,
27278     cropData : false,
27279     minWidth : 300,
27280     minHeight : 300,
27281     file : false,
27282     exif : {},
27283     baseRotate : 1,
27284     cropType : 'image/jpeg',
27285     buttons : false,
27286     canvasLoaded : false,
27287     isDocument : false,
27288     method : 'POST',
27289     paramName : 'imageUpload',
27290     loadMask : true,
27291     loadingText : 'Loading...',
27292     maskEl : false,
27293     
27294     getAutoCreate : function()
27295     {
27296         var cfg = {
27297             tag : 'div',
27298             cls : 'roo-upload-cropbox',
27299             cn : [
27300                 {
27301                     tag : 'input',
27302                     cls : 'roo-upload-cropbox-selector',
27303                     type : 'file'
27304                 },
27305                 {
27306                     tag : 'div',
27307                     cls : 'roo-upload-cropbox-body',
27308                     style : 'cursor:pointer',
27309                     cn : [
27310                         {
27311                             tag : 'div',
27312                             cls : 'roo-upload-cropbox-preview'
27313                         },
27314                         {
27315                             tag : 'div',
27316                             cls : 'roo-upload-cropbox-thumb'
27317                         },
27318                         {
27319                             tag : 'div',
27320                             cls : 'roo-upload-cropbox-empty-notify',
27321                             html : this.emptyText
27322                         },
27323                         {
27324                             tag : 'div',
27325                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27326                             html : this.rotateNotify
27327                         }
27328                     ]
27329                 },
27330                 {
27331                     tag : 'div',
27332                     cls : 'roo-upload-cropbox-footer',
27333                     cn : {
27334                         tag : 'div',
27335                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27336                         cn : []
27337                     }
27338                 }
27339             ]
27340         };
27341         
27342         return cfg;
27343     },
27344     
27345     onRender : function(ct, position)
27346     {
27347         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27348         
27349         if (this.buttons.length) {
27350             
27351             Roo.each(this.buttons, function(bb) {
27352                 
27353                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27354                 
27355                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27356                 
27357             }, this);
27358         }
27359         
27360         if(this.loadMask){
27361             this.maskEl = this.el;
27362         }
27363     },
27364     
27365     initEvents : function()
27366     {
27367         this.urlAPI = (window.createObjectURL && window) || 
27368                                 (window.URL && URL.revokeObjectURL && URL) || 
27369                                 (window.webkitURL && webkitURL);
27370                         
27371         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27372         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27373         
27374         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27375         this.selectorEl.hide();
27376         
27377         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27378         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27379         
27380         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27381         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27382         this.thumbEl.hide();
27383         
27384         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27385         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27386         
27387         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27388         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27389         this.errorEl.hide();
27390         
27391         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27392         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27393         this.footerEl.hide();
27394         
27395         this.setThumbBoxSize();
27396         
27397         this.bind();
27398         
27399         this.resize();
27400         
27401         this.fireEvent('initial', this);
27402     },
27403
27404     bind : function()
27405     {
27406         var _this = this;
27407         
27408         window.addEventListener("resize", function() { _this.resize(); } );
27409         
27410         this.bodyEl.on('click', this.beforeSelectFile, this);
27411         
27412         if(Roo.isTouch){
27413             this.bodyEl.on('touchstart', this.onTouchStart, this);
27414             this.bodyEl.on('touchmove', this.onTouchMove, this);
27415             this.bodyEl.on('touchend', this.onTouchEnd, this);
27416         }
27417         
27418         if(!Roo.isTouch){
27419             this.bodyEl.on('mousedown', this.onMouseDown, this);
27420             this.bodyEl.on('mousemove', this.onMouseMove, this);
27421             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27422             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27423             Roo.get(document).on('mouseup', this.onMouseUp, this);
27424         }
27425         
27426         this.selectorEl.on('change', this.onFileSelected, this);
27427     },
27428     
27429     reset : function()
27430     {    
27431         this.scale = 0;
27432         this.baseScale = 1;
27433         this.rotate = 0;
27434         this.baseRotate = 1;
27435         this.dragable = false;
27436         this.pinching = false;
27437         this.mouseX = 0;
27438         this.mouseY = 0;
27439         this.cropData = false;
27440         this.notifyEl.dom.innerHTML = this.emptyText;
27441         
27442         this.selectorEl.dom.value = '';
27443         
27444     },
27445     
27446     resize : function()
27447     {
27448         if(this.fireEvent('resize', this) != false){
27449             this.setThumbBoxPosition();
27450             this.setCanvasPosition();
27451         }
27452     },
27453     
27454     onFooterButtonClick : function(e, el, o, type)
27455     {
27456         switch (type) {
27457             case 'rotate-left' :
27458                 this.onRotateLeft(e);
27459                 break;
27460             case 'rotate-right' :
27461                 this.onRotateRight(e);
27462                 break;
27463             case 'picture' :
27464                 this.beforeSelectFile(e);
27465                 break;
27466             case 'trash' :
27467                 this.trash(e);
27468                 break;
27469             case 'crop' :
27470                 this.crop(e);
27471                 break;
27472             case 'download' :
27473                 this.download(e);
27474                 break;
27475             default :
27476                 break;
27477         }
27478         
27479         this.fireEvent('footerbuttonclick', this, type);
27480     },
27481     
27482     beforeSelectFile : function(e)
27483     {
27484         e.preventDefault();
27485         
27486         if(this.fireEvent('beforeselectfile', this) != false){
27487             this.selectorEl.dom.click();
27488         }
27489     },
27490     
27491     onFileSelected : function(e)
27492     {
27493         e.preventDefault();
27494         
27495         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27496             return;
27497         }
27498         
27499         var file = this.selectorEl.dom.files[0];
27500         
27501         if(this.fireEvent('inspect', this, file) != false){
27502             this.prepare(file);
27503         }
27504         
27505     },
27506     
27507     trash : function(e)
27508     {
27509         this.fireEvent('trash', this);
27510     },
27511     
27512     download : function(e)
27513     {
27514         this.fireEvent('download', this);
27515     },
27516     
27517     loadCanvas : function(src)
27518     {   
27519         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27520             
27521             this.reset();
27522             
27523             this.imageEl = document.createElement('img');
27524             
27525             var _this = this;
27526             
27527             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27528             
27529             this.imageEl.src = src;
27530         }
27531     },
27532     
27533     onLoadCanvas : function()
27534     {   
27535         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27536         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27537         
27538         this.bodyEl.un('click', this.beforeSelectFile, this);
27539         
27540         this.notifyEl.hide();
27541         this.thumbEl.show();
27542         this.footerEl.show();
27543         
27544         this.baseRotateLevel();
27545         
27546         if(this.isDocument){
27547             this.setThumbBoxSize();
27548         }
27549         
27550         this.setThumbBoxPosition();
27551         
27552         this.baseScaleLevel();
27553         
27554         this.draw();
27555         
27556         this.resize();
27557         
27558         this.canvasLoaded = true;
27559         
27560         if(this.loadMask){
27561             this.maskEl.unmask();
27562         }
27563         
27564     },
27565     
27566     setCanvasPosition : function()
27567     {   
27568         if(!this.canvasEl){
27569             return;
27570         }
27571         
27572         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27573         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27574         
27575         this.previewEl.setLeft(pw);
27576         this.previewEl.setTop(ph);
27577         
27578     },
27579     
27580     onMouseDown : function(e)
27581     {   
27582         e.stopEvent();
27583         
27584         this.dragable = true;
27585         this.pinching = false;
27586         
27587         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27588             this.dragable = false;
27589             return;
27590         }
27591         
27592         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27593         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27594         
27595     },
27596     
27597     onMouseMove : function(e)
27598     {   
27599         e.stopEvent();
27600         
27601         if(!this.canvasLoaded){
27602             return;
27603         }
27604         
27605         if (!this.dragable){
27606             return;
27607         }
27608         
27609         var minX = Math.ceil(this.thumbEl.getLeft(true));
27610         var minY = Math.ceil(this.thumbEl.getTop(true));
27611         
27612         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27613         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27614         
27615         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27616         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27617         
27618         x = x - this.mouseX;
27619         y = y - this.mouseY;
27620         
27621         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27622         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27623         
27624         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27625         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27626         
27627         this.previewEl.setLeft(bgX);
27628         this.previewEl.setTop(bgY);
27629         
27630         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27631         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27632     },
27633     
27634     onMouseUp : function(e)
27635     {   
27636         e.stopEvent();
27637         
27638         this.dragable = false;
27639     },
27640     
27641     onMouseWheel : function(e)
27642     {   
27643         e.stopEvent();
27644         
27645         this.startScale = this.scale;
27646         
27647         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27648         
27649         if(!this.zoomable()){
27650             this.scale = this.startScale;
27651             return;
27652         }
27653         
27654         this.draw();
27655         
27656         return;
27657     },
27658     
27659     zoomable : function()
27660     {
27661         var minScale = this.thumbEl.getWidth() / this.minWidth;
27662         
27663         if(this.minWidth < this.minHeight){
27664             minScale = this.thumbEl.getHeight() / this.minHeight;
27665         }
27666         
27667         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27668         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27669         
27670         if(
27671                 this.isDocument &&
27672                 (this.rotate == 0 || this.rotate == 180) && 
27673                 (
27674                     width > this.imageEl.OriginWidth || 
27675                     height > this.imageEl.OriginHeight ||
27676                     (width < this.minWidth && height < this.minHeight)
27677                 )
27678         ){
27679             return false;
27680         }
27681         
27682         if(
27683                 this.isDocument &&
27684                 (this.rotate == 90 || this.rotate == 270) && 
27685                 (
27686                     width > this.imageEl.OriginWidth || 
27687                     height > this.imageEl.OriginHeight ||
27688                     (width < this.minHeight && height < this.minWidth)
27689                 )
27690         ){
27691             return false;
27692         }
27693         
27694         if(
27695                 !this.isDocument &&
27696                 (this.rotate == 0 || this.rotate == 180) && 
27697                 (
27698                     width < this.minWidth || 
27699                     width > this.imageEl.OriginWidth || 
27700                     height < this.minHeight || 
27701                     height > this.imageEl.OriginHeight
27702                 )
27703         ){
27704             return false;
27705         }
27706         
27707         if(
27708                 !this.isDocument &&
27709                 (this.rotate == 90 || this.rotate == 270) && 
27710                 (
27711                     width < this.minHeight || 
27712                     width > this.imageEl.OriginWidth || 
27713                     height < this.minWidth || 
27714                     height > this.imageEl.OriginHeight
27715                 )
27716         ){
27717             return false;
27718         }
27719         
27720         return true;
27721         
27722     },
27723     
27724     onRotateLeft : function(e)
27725     {   
27726         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27727             
27728             var minScale = this.thumbEl.getWidth() / this.minWidth;
27729             
27730             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27731             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27732             
27733             this.startScale = this.scale;
27734             
27735             while (this.getScaleLevel() < minScale){
27736             
27737                 this.scale = this.scale + 1;
27738                 
27739                 if(!this.zoomable()){
27740                     break;
27741                 }
27742                 
27743                 if(
27744                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27745                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27746                 ){
27747                     continue;
27748                 }
27749                 
27750                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27751
27752                 this.draw();
27753                 
27754                 return;
27755             }
27756             
27757             this.scale = this.startScale;
27758             
27759             this.onRotateFail();
27760             
27761             return false;
27762         }
27763         
27764         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27765
27766         if(this.isDocument){
27767             this.setThumbBoxSize();
27768             this.setThumbBoxPosition();
27769             this.setCanvasPosition();
27770         }
27771         
27772         this.draw();
27773         
27774         this.fireEvent('rotate', this, 'left');
27775         
27776     },
27777     
27778     onRotateRight : function(e)
27779     {
27780         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27781             
27782             var minScale = this.thumbEl.getWidth() / this.minWidth;
27783         
27784             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27785             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27786             
27787             this.startScale = this.scale;
27788             
27789             while (this.getScaleLevel() < minScale){
27790             
27791                 this.scale = this.scale + 1;
27792                 
27793                 if(!this.zoomable()){
27794                     break;
27795                 }
27796                 
27797                 if(
27798                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27799                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27800                 ){
27801                     continue;
27802                 }
27803                 
27804                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27805
27806                 this.draw();
27807                 
27808                 return;
27809             }
27810             
27811             this.scale = this.startScale;
27812             
27813             this.onRotateFail();
27814             
27815             return false;
27816         }
27817         
27818         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27819
27820         if(this.isDocument){
27821             this.setThumbBoxSize();
27822             this.setThumbBoxPosition();
27823             this.setCanvasPosition();
27824         }
27825         
27826         this.draw();
27827         
27828         this.fireEvent('rotate', this, 'right');
27829     },
27830     
27831     onRotateFail : function()
27832     {
27833         this.errorEl.show(true);
27834         
27835         var _this = this;
27836         
27837         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27838     },
27839     
27840     draw : function()
27841     {
27842         this.previewEl.dom.innerHTML = '';
27843         
27844         var canvasEl = document.createElement("canvas");
27845         
27846         var contextEl = canvasEl.getContext("2d");
27847         
27848         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27849         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27850         var center = this.imageEl.OriginWidth / 2;
27851         
27852         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27853             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27854             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27855             center = this.imageEl.OriginHeight / 2;
27856         }
27857         
27858         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27859         
27860         contextEl.translate(center, center);
27861         contextEl.rotate(this.rotate * Math.PI / 180);
27862
27863         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27864         
27865         this.canvasEl = document.createElement("canvas");
27866         
27867         this.contextEl = this.canvasEl.getContext("2d");
27868         
27869         switch (this.rotate) {
27870             case 0 :
27871                 
27872                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27873                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27874                 
27875                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27876                 
27877                 break;
27878             case 90 : 
27879                 
27880                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27881                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27882                 
27883                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27884                     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);
27885                     break;
27886                 }
27887                 
27888                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27889                 
27890                 break;
27891             case 180 :
27892                 
27893                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27894                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27895                 
27896                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27897                     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);
27898                     break;
27899                 }
27900                 
27901                 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);
27902                 
27903                 break;
27904             case 270 :
27905                 
27906                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27907                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27908         
27909                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27910                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27911                     break;
27912                 }
27913                 
27914                 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);
27915                 
27916                 break;
27917             default : 
27918                 break;
27919         }
27920         
27921         this.previewEl.appendChild(this.canvasEl);
27922         
27923         this.setCanvasPosition();
27924     },
27925     
27926     crop : function()
27927     {
27928         if(!this.canvasLoaded){
27929             return;
27930         }
27931         
27932         var imageCanvas = document.createElement("canvas");
27933         
27934         var imageContext = imageCanvas.getContext("2d");
27935         
27936         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27937         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27938         
27939         var center = imageCanvas.width / 2;
27940         
27941         imageContext.translate(center, center);
27942         
27943         imageContext.rotate(this.rotate * Math.PI / 180);
27944         
27945         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27946         
27947         var canvas = document.createElement("canvas");
27948         
27949         var context = canvas.getContext("2d");
27950                 
27951         canvas.width = this.minWidth;
27952         canvas.height = this.minHeight;
27953
27954         switch (this.rotate) {
27955             case 0 :
27956                 
27957                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27958                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27959                 
27960                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27961                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27962                 
27963                 var targetWidth = this.minWidth - 2 * x;
27964                 var targetHeight = this.minHeight - 2 * y;
27965                 
27966                 var scale = 1;
27967                 
27968                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27969                     scale = targetWidth / width;
27970                 }
27971                 
27972                 if(x > 0 && y == 0){
27973                     scale = targetHeight / height;
27974                 }
27975                 
27976                 if(x > 0 && y > 0){
27977                     scale = targetWidth / width;
27978                     
27979                     if(width < height){
27980                         scale = targetHeight / height;
27981                     }
27982                 }
27983                 
27984                 context.scale(scale, scale);
27985                 
27986                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27987                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27988
27989                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27990                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27991
27992                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27993                 
27994                 break;
27995             case 90 : 
27996                 
27997                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27998                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27999                 
28000                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28001                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28002                 
28003                 var targetWidth = this.minWidth - 2 * x;
28004                 var targetHeight = this.minHeight - 2 * y;
28005                 
28006                 var scale = 1;
28007                 
28008                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28009                     scale = targetWidth / width;
28010                 }
28011                 
28012                 if(x > 0 && y == 0){
28013                     scale = targetHeight / height;
28014                 }
28015                 
28016                 if(x > 0 && y > 0){
28017                     scale = targetWidth / width;
28018                     
28019                     if(width < height){
28020                         scale = targetHeight / height;
28021                     }
28022                 }
28023                 
28024                 context.scale(scale, scale);
28025                 
28026                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28027                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28028
28029                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28030                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28031                 
28032                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28033                 
28034                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28035                 
28036                 break;
28037             case 180 :
28038                 
28039                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28040                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28041                 
28042                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28043                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28044                 
28045                 var targetWidth = this.minWidth - 2 * x;
28046                 var targetHeight = this.minHeight - 2 * y;
28047                 
28048                 var scale = 1;
28049                 
28050                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28051                     scale = targetWidth / width;
28052                 }
28053                 
28054                 if(x > 0 && y == 0){
28055                     scale = targetHeight / height;
28056                 }
28057                 
28058                 if(x > 0 && y > 0){
28059                     scale = targetWidth / width;
28060                     
28061                     if(width < height){
28062                         scale = targetHeight / height;
28063                     }
28064                 }
28065                 
28066                 context.scale(scale, scale);
28067                 
28068                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28069                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28070
28071                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28072                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28073
28074                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28075                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28076                 
28077                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28078                 
28079                 break;
28080             case 270 :
28081                 
28082                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28083                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28084                 
28085                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28086                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28087                 
28088                 var targetWidth = this.minWidth - 2 * x;
28089                 var targetHeight = this.minHeight - 2 * y;
28090                 
28091                 var scale = 1;
28092                 
28093                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28094                     scale = targetWidth / width;
28095                 }
28096                 
28097                 if(x > 0 && y == 0){
28098                     scale = targetHeight / height;
28099                 }
28100                 
28101                 if(x > 0 && y > 0){
28102                     scale = targetWidth / width;
28103                     
28104                     if(width < height){
28105                         scale = targetHeight / height;
28106                     }
28107                 }
28108                 
28109                 context.scale(scale, scale);
28110                 
28111                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28112                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28113
28114                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28115                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28116                 
28117                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28118                 
28119                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28120                 
28121                 break;
28122             default : 
28123                 break;
28124         }
28125         
28126         this.cropData = canvas.toDataURL(this.cropType);
28127         
28128         if(this.fireEvent('crop', this, this.cropData) !== false){
28129             this.process(this.file, this.cropData);
28130         }
28131         
28132         return;
28133         
28134     },
28135     
28136     setThumbBoxSize : function()
28137     {
28138         var width, height;
28139         
28140         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28141             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28142             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28143             
28144             this.minWidth = width;
28145             this.minHeight = height;
28146             
28147             if(this.rotate == 90 || this.rotate == 270){
28148                 this.minWidth = height;
28149                 this.minHeight = width;
28150             }
28151         }
28152         
28153         height = 300;
28154         width = Math.ceil(this.minWidth * height / this.minHeight);
28155         
28156         if(this.minWidth > this.minHeight){
28157             width = 300;
28158             height = Math.ceil(this.minHeight * width / this.minWidth);
28159         }
28160         
28161         this.thumbEl.setStyle({
28162             width : width + 'px',
28163             height : height + 'px'
28164         });
28165
28166         return;
28167             
28168     },
28169     
28170     setThumbBoxPosition : function()
28171     {
28172         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28173         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28174         
28175         this.thumbEl.setLeft(x);
28176         this.thumbEl.setTop(y);
28177         
28178     },
28179     
28180     baseRotateLevel : function()
28181     {
28182         this.baseRotate = 1;
28183         
28184         if(
28185                 typeof(this.exif) != 'undefined' &&
28186                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28187                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28188         ){
28189             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28190         }
28191         
28192         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28193         
28194     },
28195     
28196     baseScaleLevel : function()
28197     {
28198         var width, height;
28199         
28200         if(this.isDocument){
28201             
28202             if(this.baseRotate == 6 || this.baseRotate == 8){
28203             
28204                 height = this.thumbEl.getHeight();
28205                 this.baseScale = height / this.imageEl.OriginWidth;
28206
28207                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28208                     width = this.thumbEl.getWidth();
28209                     this.baseScale = width / this.imageEl.OriginHeight;
28210                 }
28211
28212                 return;
28213             }
28214
28215             height = this.thumbEl.getHeight();
28216             this.baseScale = height / this.imageEl.OriginHeight;
28217
28218             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28219                 width = this.thumbEl.getWidth();
28220                 this.baseScale = width / this.imageEl.OriginWidth;
28221             }
28222
28223             return;
28224         }
28225         
28226         if(this.baseRotate == 6 || this.baseRotate == 8){
28227             
28228             width = this.thumbEl.getHeight();
28229             this.baseScale = width / this.imageEl.OriginHeight;
28230             
28231             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28232                 height = this.thumbEl.getWidth();
28233                 this.baseScale = height / this.imageEl.OriginHeight;
28234             }
28235             
28236             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28237                 height = this.thumbEl.getWidth();
28238                 this.baseScale = height / this.imageEl.OriginHeight;
28239                 
28240                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28241                     width = this.thumbEl.getHeight();
28242                     this.baseScale = width / this.imageEl.OriginWidth;
28243                 }
28244             }
28245             
28246             return;
28247         }
28248         
28249         width = this.thumbEl.getWidth();
28250         this.baseScale = width / this.imageEl.OriginWidth;
28251         
28252         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28253             height = this.thumbEl.getHeight();
28254             this.baseScale = height / this.imageEl.OriginHeight;
28255         }
28256         
28257         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28258             
28259             height = this.thumbEl.getHeight();
28260             this.baseScale = height / this.imageEl.OriginHeight;
28261             
28262             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28263                 width = this.thumbEl.getWidth();
28264                 this.baseScale = width / this.imageEl.OriginWidth;
28265             }
28266             
28267         }
28268         
28269         return;
28270     },
28271     
28272     getScaleLevel : function()
28273     {
28274         return this.baseScale * Math.pow(1.1, this.scale);
28275     },
28276     
28277     onTouchStart : function(e)
28278     {
28279         if(!this.canvasLoaded){
28280             this.beforeSelectFile(e);
28281             return;
28282         }
28283         
28284         var touches = e.browserEvent.touches;
28285         
28286         if(!touches){
28287             return;
28288         }
28289         
28290         if(touches.length == 1){
28291             this.onMouseDown(e);
28292             return;
28293         }
28294         
28295         if(touches.length != 2){
28296             return;
28297         }
28298         
28299         var coords = [];
28300         
28301         for(var i = 0, finger; finger = touches[i]; i++){
28302             coords.push(finger.pageX, finger.pageY);
28303         }
28304         
28305         var x = Math.pow(coords[0] - coords[2], 2);
28306         var y = Math.pow(coords[1] - coords[3], 2);
28307         
28308         this.startDistance = Math.sqrt(x + y);
28309         
28310         this.startScale = this.scale;
28311         
28312         this.pinching = true;
28313         this.dragable = false;
28314         
28315     },
28316     
28317     onTouchMove : function(e)
28318     {
28319         if(!this.pinching && !this.dragable){
28320             return;
28321         }
28322         
28323         var touches = e.browserEvent.touches;
28324         
28325         if(!touches){
28326             return;
28327         }
28328         
28329         if(this.dragable){
28330             this.onMouseMove(e);
28331             return;
28332         }
28333         
28334         var coords = [];
28335         
28336         for(var i = 0, finger; finger = touches[i]; i++){
28337             coords.push(finger.pageX, finger.pageY);
28338         }
28339         
28340         var x = Math.pow(coords[0] - coords[2], 2);
28341         var y = Math.pow(coords[1] - coords[3], 2);
28342         
28343         this.endDistance = Math.sqrt(x + y);
28344         
28345         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28346         
28347         if(!this.zoomable()){
28348             this.scale = this.startScale;
28349             return;
28350         }
28351         
28352         this.draw();
28353         
28354     },
28355     
28356     onTouchEnd : function(e)
28357     {
28358         this.pinching = false;
28359         this.dragable = false;
28360         
28361     },
28362     
28363     process : function(file, crop)
28364     {
28365         if(this.loadMask){
28366             this.maskEl.mask(this.loadingText);
28367         }
28368         
28369         this.xhr = new XMLHttpRequest();
28370         
28371         file.xhr = this.xhr;
28372
28373         this.xhr.open(this.method, this.url, true);
28374         
28375         var headers = {
28376             "Accept": "application/json",
28377             "Cache-Control": "no-cache",
28378             "X-Requested-With": "XMLHttpRequest"
28379         };
28380         
28381         for (var headerName in headers) {
28382             var headerValue = headers[headerName];
28383             if (headerValue) {
28384                 this.xhr.setRequestHeader(headerName, headerValue);
28385             }
28386         }
28387         
28388         var _this = this;
28389         
28390         this.xhr.onload = function()
28391         {
28392             _this.xhrOnLoad(_this.xhr);
28393         }
28394         
28395         this.xhr.onerror = function()
28396         {
28397             _this.xhrOnError(_this.xhr);
28398         }
28399         
28400         var formData = new FormData();
28401
28402         formData.append('returnHTML', 'NO');
28403         
28404         if(crop){
28405             formData.append('crop', crop);
28406         }
28407         
28408         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28409             formData.append(this.paramName, file, file.name);
28410         }
28411         
28412         if(typeof(file.filename) != 'undefined'){
28413             formData.append('filename', file.filename);
28414         }
28415         
28416         if(typeof(file.mimetype) != 'undefined'){
28417             formData.append('mimetype', file.mimetype);
28418         }
28419         
28420         if(this.fireEvent('arrange', this, formData) != false){
28421             this.xhr.send(formData);
28422         };
28423     },
28424     
28425     xhrOnLoad : function(xhr)
28426     {
28427         if(this.loadMask){
28428             this.maskEl.unmask();
28429         }
28430         
28431         if (xhr.readyState !== 4) {
28432             this.fireEvent('exception', this, xhr);
28433             return;
28434         }
28435
28436         var response = Roo.decode(xhr.responseText);
28437         
28438         if(!response.success){
28439             this.fireEvent('exception', this, xhr);
28440             return;
28441         }
28442         
28443         var response = Roo.decode(xhr.responseText);
28444         
28445         this.fireEvent('upload', this, response);
28446         
28447     },
28448     
28449     xhrOnError : function()
28450     {
28451         if(this.loadMask){
28452             this.maskEl.unmask();
28453         }
28454         
28455         Roo.log('xhr on error');
28456         
28457         var response = Roo.decode(xhr.responseText);
28458           
28459         Roo.log(response);
28460         
28461     },
28462     
28463     prepare : function(file)
28464     {   
28465         if(this.loadMask){
28466             this.maskEl.mask(this.loadingText);
28467         }
28468         
28469         this.file = false;
28470         this.exif = {};
28471         
28472         if(typeof(file) === 'string'){
28473             this.loadCanvas(file);
28474             return;
28475         }
28476         
28477         if(!file || !this.urlAPI){
28478             return;
28479         }
28480         
28481         this.file = file;
28482         this.cropType = file.type;
28483         
28484         var _this = this;
28485         
28486         if(this.fireEvent('prepare', this, this.file) != false){
28487             
28488             var reader = new FileReader();
28489             
28490             reader.onload = function (e) {
28491                 if (e.target.error) {
28492                     Roo.log(e.target.error);
28493                     return;
28494                 }
28495                 
28496                 var buffer = e.target.result,
28497                     dataView = new DataView(buffer),
28498                     offset = 2,
28499                     maxOffset = dataView.byteLength - 4,
28500                     markerBytes,
28501                     markerLength;
28502                 
28503                 if (dataView.getUint16(0) === 0xffd8) {
28504                     while (offset < maxOffset) {
28505                         markerBytes = dataView.getUint16(offset);
28506                         
28507                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28508                             markerLength = dataView.getUint16(offset + 2) + 2;
28509                             if (offset + markerLength > dataView.byteLength) {
28510                                 Roo.log('Invalid meta data: Invalid segment size.');
28511                                 break;
28512                             }
28513                             
28514                             if(markerBytes == 0xffe1){
28515                                 _this.parseExifData(
28516                                     dataView,
28517                                     offset,
28518                                     markerLength
28519                                 );
28520                             }
28521                             
28522                             offset += markerLength;
28523                             
28524                             continue;
28525                         }
28526                         
28527                         break;
28528                     }
28529                     
28530                 }
28531                 
28532                 var url = _this.urlAPI.createObjectURL(_this.file);
28533                 
28534                 _this.loadCanvas(url);
28535                 
28536                 return;
28537             }
28538             
28539             reader.readAsArrayBuffer(this.file);
28540             
28541         }
28542         
28543     },
28544     
28545     parseExifData : function(dataView, offset, length)
28546     {
28547         var tiffOffset = offset + 10,
28548             littleEndian,
28549             dirOffset;
28550     
28551         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28552             // No Exif data, might be XMP data instead
28553             return;
28554         }
28555         
28556         // Check for the ASCII code for "Exif" (0x45786966):
28557         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28558             // No Exif data, might be XMP data instead
28559             return;
28560         }
28561         if (tiffOffset + 8 > dataView.byteLength) {
28562             Roo.log('Invalid Exif data: Invalid segment size.');
28563             return;
28564         }
28565         // Check for the two null bytes:
28566         if (dataView.getUint16(offset + 8) !== 0x0000) {
28567             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28568             return;
28569         }
28570         // Check the byte alignment:
28571         switch (dataView.getUint16(tiffOffset)) {
28572         case 0x4949:
28573             littleEndian = true;
28574             break;
28575         case 0x4D4D:
28576             littleEndian = false;
28577             break;
28578         default:
28579             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28580             return;
28581         }
28582         // Check for the TIFF tag marker (0x002A):
28583         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28584             Roo.log('Invalid Exif data: Missing TIFF marker.');
28585             return;
28586         }
28587         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28588         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28589         
28590         this.parseExifTags(
28591             dataView,
28592             tiffOffset,
28593             tiffOffset + dirOffset,
28594             littleEndian
28595         );
28596     },
28597     
28598     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28599     {
28600         var tagsNumber,
28601             dirEndOffset,
28602             i;
28603         if (dirOffset + 6 > dataView.byteLength) {
28604             Roo.log('Invalid Exif data: Invalid directory offset.');
28605             return;
28606         }
28607         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28608         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28609         if (dirEndOffset + 4 > dataView.byteLength) {
28610             Roo.log('Invalid Exif data: Invalid directory size.');
28611             return;
28612         }
28613         for (i = 0; i < tagsNumber; i += 1) {
28614             this.parseExifTag(
28615                 dataView,
28616                 tiffOffset,
28617                 dirOffset + 2 + 12 * i, // tag offset
28618                 littleEndian
28619             );
28620         }
28621         // Return the offset to the next directory:
28622         return dataView.getUint32(dirEndOffset, littleEndian);
28623     },
28624     
28625     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28626     {
28627         var tag = dataView.getUint16(offset, littleEndian);
28628         
28629         this.exif[tag] = this.getExifValue(
28630             dataView,
28631             tiffOffset,
28632             offset,
28633             dataView.getUint16(offset + 2, littleEndian), // tag type
28634             dataView.getUint32(offset + 4, littleEndian), // tag length
28635             littleEndian
28636         );
28637     },
28638     
28639     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28640     {
28641         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28642             tagSize,
28643             dataOffset,
28644             values,
28645             i,
28646             str,
28647             c;
28648     
28649         if (!tagType) {
28650             Roo.log('Invalid Exif data: Invalid tag type.');
28651             return;
28652         }
28653         
28654         tagSize = tagType.size * length;
28655         // Determine if the value is contained in the dataOffset bytes,
28656         // or if the value at the dataOffset is a pointer to the actual data:
28657         dataOffset = tagSize > 4 ?
28658                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28659         if (dataOffset + tagSize > dataView.byteLength) {
28660             Roo.log('Invalid Exif data: Invalid data offset.');
28661             return;
28662         }
28663         if (length === 1) {
28664             return tagType.getValue(dataView, dataOffset, littleEndian);
28665         }
28666         values = [];
28667         for (i = 0; i < length; i += 1) {
28668             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28669         }
28670         
28671         if (tagType.ascii) {
28672             str = '';
28673             // Concatenate the chars:
28674             for (i = 0; i < values.length; i += 1) {
28675                 c = values[i];
28676                 // Ignore the terminating NULL byte(s):
28677                 if (c === '\u0000') {
28678                     break;
28679                 }
28680                 str += c;
28681             }
28682             return str;
28683         }
28684         return values;
28685     }
28686     
28687 });
28688
28689 Roo.apply(Roo.bootstrap.UploadCropbox, {
28690     tags : {
28691         'Orientation': 0x0112
28692     },
28693     
28694     Orientation: {
28695             1: 0, //'top-left',
28696 //            2: 'top-right',
28697             3: 180, //'bottom-right',
28698 //            4: 'bottom-left',
28699 //            5: 'left-top',
28700             6: 90, //'right-top',
28701 //            7: 'right-bottom',
28702             8: 270 //'left-bottom'
28703     },
28704     
28705     exifTagTypes : {
28706         // byte, 8-bit unsigned int:
28707         1: {
28708             getValue: function (dataView, dataOffset) {
28709                 return dataView.getUint8(dataOffset);
28710             },
28711             size: 1
28712         },
28713         // ascii, 8-bit byte:
28714         2: {
28715             getValue: function (dataView, dataOffset) {
28716                 return String.fromCharCode(dataView.getUint8(dataOffset));
28717             },
28718             size: 1,
28719             ascii: true
28720         },
28721         // short, 16 bit int:
28722         3: {
28723             getValue: function (dataView, dataOffset, littleEndian) {
28724                 return dataView.getUint16(dataOffset, littleEndian);
28725             },
28726             size: 2
28727         },
28728         // long, 32 bit int:
28729         4: {
28730             getValue: function (dataView, dataOffset, littleEndian) {
28731                 return dataView.getUint32(dataOffset, littleEndian);
28732             },
28733             size: 4
28734         },
28735         // rational = two long values, first is numerator, second is denominator:
28736         5: {
28737             getValue: function (dataView, dataOffset, littleEndian) {
28738                 return dataView.getUint32(dataOffset, littleEndian) /
28739                     dataView.getUint32(dataOffset + 4, littleEndian);
28740             },
28741             size: 8
28742         },
28743         // slong, 32 bit signed int:
28744         9: {
28745             getValue: function (dataView, dataOffset, littleEndian) {
28746                 return dataView.getInt32(dataOffset, littleEndian);
28747             },
28748             size: 4
28749         },
28750         // srational, two slongs, first is numerator, second is denominator:
28751         10: {
28752             getValue: function (dataView, dataOffset, littleEndian) {
28753                 return dataView.getInt32(dataOffset, littleEndian) /
28754                     dataView.getInt32(dataOffset + 4, littleEndian);
28755             },
28756             size: 8
28757         }
28758     },
28759     
28760     footer : {
28761         STANDARD : [
28762             {
28763                 tag : 'div',
28764                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28765                 action : 'rotate-left',
28766                 cn : [
28767                     {
28768                         tag : 'button',
28769                         cls : 'btn btn-default',
28770                         html : '<i class="fa fa-undo"></i>'
28771                     }
28772                 ]
28773             },
28774             {
28775                 tag : 'div',
28776                 cls : 'btn-group roo-upload-cropbox-picture',
28777                 action : 'picture',
28778                 cn : [
28779                     {
28780                         tag : 'button',
28781                         cls : 'btn btn-default',
28782                         html : '<i class="fa fa-picture-o"></i>'
28783                     }
28784                 ]
28785             },
28786             {
28787                 tag : 'div',
28788                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28789                 action : 'rotate-right',
28790                 cn : [
28791                     {
28792                         tag : 'button',
28793                         cls : 'btn btn-default',
28794                         html : '<i class="fa fa-repeat"></i>'
28795                     }
28796                 ]
28797             }
28798         ],
28799         DOCUMENT : [
28800             {
28801                 tag : 'div',
28802                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28803                 action : 'rotate-left',
28804                 cn : [
28805                     {
28806                         tag : 'button',
28807                         cls : 'btn btn-default',
28808                         html : '<i class="fa fa-undo"></i>'
28809                     }
28810                 ]
28811             },
28812             {
28813                 tag : 'div',
28814                 cls : 'btn-group roo-upload-cropbox-download',
28815                 action : 'download',
28816                 cn : [
28817                     {
28818                         tag : 'button',
28819                         cls : 'btn btn-default',
28820                         html : '<i class="fa fa-download"></i>'
28821                     }
28822                 ]
28823             },
28824             {
28825                 tag : 'div',
28826                 cls : 'btn-group roo-upload-cropbox-crop',
28827                 action : 'crop',
28828                 cn : [
28829                     {
28830                         tag : 'button',
28831                         cls : 'btn btn-default',
28832                         html : '<i class="fa fa-crop"></i>'
28833                     }
28834                 ]
28835             },
28836             {
28837                 tag : 'div',
28838                 cls : 'btn-group roo-upload-cropbox-trash',
28839                 action : 'trash',
28840                 cn : [
28841                     {
28842                         tag : 'button',
28843                         cls : 'btn btn-default',
28844                         html : '<i class="fa fa-trash"></i>'
28845                     }
28846                 ]
28847             },
28848             {
28849                 tag : 'div',
28850                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28851                 action : 'rotate-right',
28852                 cn : [
28853                     {
28854                         tag : 'button',
28855                         cls : 'btn btn-default',
28856                         html : '<i class="fa fa-repeat"></i>'
28857                     }
28858                 ]
28859             }
28860         ],
28861         ROTATOR : [
28862             {
28863                 tag : 'div',
28864                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28865                 action : 'rotate-left',
28866                 cn : [
28867                     {
28868                         tag : 'button',
28869                         cls : 'btn btn-default',
28870                         html : '<i class="fa fa-undo"></i>'
28871                     }
28872                 ]
28873             },
28874             {
28875                 tag : 'div',
28876                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28877                 action : 'rotate-right',
28878                 cn : [
28879                     {
28880                         tag : 'button',
28881                         cls : 'btn btn-default',
28882                         html : '<i class="fa fa-repeat"></i>'
28883                     }
28884                 ]
28885             }
28886         ]
28887     }
28888 });
28889
28890 /*
28891 * Licence: LGPL
28892 */
28893
28894 /**
28895  * @class Roo.bootstrap.DocumentManager
28896  * @extends Roo.bootstrap.Component
28897  * Bootstrap DocumentManager class
28898  * @cfg {String} paramName default 'imageUpload'
28899  * @cfg {String} toolTipName default 'filename'
28900  * @cfg {String} method default POST
28901  * @cfg {String} url action url
28902  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28903  * @cfg {Boolean} multiple multiple upload default true
28904  * @cfg {Number} thumbSize default 300
28905  * @cfg {String} fieldLabel
28906  * @cfg {Number} labelWidth default 4
28907  * @cfg {String} labelAlign (left|top) default left
28908  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28909 * @cfg {Number} labellg set the width of label (1-12)
28910  * @cfg {Number} labelmd set the width of label (1-12)
28911  * @cfg {Number} labelsm set the width of label (1-12)
28912  * @cfg {Number} labelxs set the width of label (1-12)
28913  * 
28914  * @constructor
28915  * Create a new DocumentManager
28916  * @param {Object} config The config object
28917  */
28918
28919 Roo.bootstrap.DocumentManager = function(config){
28920     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28921     
28922     this.files = [];
28923     this.delegates = [];
28924     
28925     this.addEvents({
28926         /**
28927          * @event initial
28928          * Fire when initial the DocumentManager
28929          * @param {Roo.bootstrap.DocumentManager} this
28930          */
28931         "initial" : true,
28932         /**
28933          * @event inspect
28934          * inspect selected file
28935          * @param {Roo.bootstrap.DocumentManager} this
28936          * @param {File} file
28937          */
28938         "inspect" : true,
28939         /**
28940          * @event exception
28941          * Fire when xhr load exception
28942          * @param {Roo.bootstrap.DocumentManager} this
28943          * @param {XMLHttpRequest} xhr
28944          */
28945         "exception" : true,
28946         /**
28947          * @event afterupload
28948          * Fire when xhr load exception
28949          * @param {Roo.bootstrap.DocumentManager} this
28950          * @param {XMLHttpRequest} xhr
28951          */
28952         "afterupload" : true,
28953         /**
28954          * @event prepare
28955          * prepare the form data
28956          * @param {Roo.bootstrap.DocumentManager} this
28957          * @param {Object} formData
28958          */
28959         "prepare" : true,
28960         /**
28961          * @event remove
28962          * Fire when remove the file
28963          * @param {Roo.bootstrap.DocumentManager} this
28964          * @param {Object} file
28965          */
28966         "remove" : true,
28967         /**
28968          * @event refresh
28969          * Fire after refresh the file
28970          * @param {Roo.bootstrap.DocumentManager} this
28971          */
28972         "refresh" : true,
28973         /**
28974          * @event click
28975          * Fire after click the image
28976          * @param {Roo.bootstrap.DocumentManager} this
28977          * @param {Object} file
28978          */
28979         "click" : true,
28980         /**
28981          * @event edit
28982          * Fire when upload a image and editable set to true
28983          * @param {Roo.bootstrap.DocumentManager} this
28984          * @param {Object} file
28985          */
28986         "edit" : true,
28987         /**
28988          * @event beforeselectfile
28989          * Fire before select file
28990          * @param {Roo.bootstrap.DocumentManager} this
28991          */
28992         "beforeselectfile" : true,
28993         /**
28994          * @event process
28995          * Fire before process file
28996          * @param {Roo.bootstrap.DocumentManager} this
28997          * @param {Object} file
28998          */
28999         "process" : true,
29000         /**
29001          * @event previewrendered
29002          * Fire when preview rendered
29003          * @param {Roo.bootstrap.DocumentManager} this
29004          * @param {Object} file
29005          */
29006         "previewrendered" : true,
29007         /**
29008          */
29009         "previewResize" : true
29010         
29011     });
29012 };
29013
29014 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29015     
29016     boxes : 0,
29017     inputName : '',
29018     thumbSize : 300,
29019     multiple : true,
29020     files : false,
29021     method : 'POST',
29022     url : '',
29023     paramName : 'imageUpload',
29024     toolTipName : 'filename',
29025     fieldLabel : '',
29026     labelWidth : 4,
29027     labelAlign : 'left',
29028     editable : true,
29029     delegates : false,
29030     xhr : false, 
29031     
29032     labellg : 0,
29033     labelmd : 0,
29034     labelsm : 0,
29035     labelxs : 0,
29036     
29037     getAutoCreate : function()
29038     {   
29039         var managerWidget = {
29040             tag : 'div',
29041             cls : 'roo-document-manager',
29042             cn : [
29043                 {
29044                     tag : 'input',
29045                     cls : 'roo-document-manager-selector',
29046                     type : 'file'
29047                 },
29048                 {
29049                     tag : 'div',
29050                     cls : 'roo-document-manager-uploader',
29051                     cn : [
29052                         {
29053                             tag : 'div',
29054                             cls : 'roo-document-manager-upload-btn',
29055                             html : '<i class="fa fa-plus"></i>'
29056                         }
29057                     ]
29058                     
29059                 }
29060             ]
29061         };
29062         
29063         var content = [
29064             {
29065                 tag : 'div',
29066                 cls : 'column col-md-12',
29067                 cn : managerWidget
29068             }
29069         ];
29070         
29071         if(this.fieldLabel.length){
29072             
29073             content = [
29074                 {
29075                     tag : 'div',
29076                     cls : 'column col-md-12',
29077                     html : this.fieldLabel
29078                 },
29079                 {
29080                     tag : 'div',
29081                     cls : 'column col-md-12',
29082                     cn : managerWidget
29083                 }
29084             ];
29085
29086             if(this.labelAlign == 'left'){
29087                 content = [
29088                     {
29089                         tag : 'div',
29090                         cls : 'column',
29091                         html : this.fieldLabel
29092                     },
29093                     {
29094                         tag : 'div',
29095                         cls : 'column',
29096                         cn : managerWidget
29097                     }
29098                 ];
29099                 
29100                 if(this.labelWidth > 12){
29101                     content[0].style = "width: " + this.labelWidth + 'px';
29102                 }
29103
29104                 if(this.labelWidth < 13 && this.labelmd == 0){
29105                     this.labelmd = this.labelWidth;
29106                 }
29107
29108                 if(this.labellg > 0){
29109                     content[0].cls += ' col-lg-' + this.labellg;
29110                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29111                 }
29112
29113                 if(this.labelmd > 0){
29114                     content[0].cls += ' col-md-' + this.labelmd;
29115                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29116                 }
29117
29118                 if(this.labelsm > 0){
29119                     content[0].cls += ' col-sm-' + this.labelsm;
29120                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29121                 }
29122
29123                 if(this.labelxs > 0){
29124                     content[0].cls += ' col-xs-' + this.labelxs;
29125                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29126                 }
29127                 
29128             }
29129         }
29130         
29131         var cfg = {
29132             tag : 'div',
29133             cls : 'row clearfix',
29134             cn : content
29135         };
29136         
29137         return cfg;
29138         
29139     },
29140     
29141     initEvents : function()
29142     {
29143         this.managerEl = this.el.select('.roo-document-manager', true).first();
29144         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29145         
29146         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29147         this.selectorEl.hide();
29148         
29149         if(this.multiple){
29150             this.selectorEl.attr('multiple', 'multiple');
29151         }
29152         
29153         this.selectorEl.on('change', this.onFileSelected, this);
29154         
29155         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29156         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29157         
29158         this.uploader.on('click', this.onUploaderClick, this);
29159         
29160         this.renderProgressDialog();
29161         
29162         var _this = this;
29163         
29164         window.addEventListener("resize", function() { _this.refresh(); } );
29165         
29166         this.fireEvent('initial', this);
29167     },
29168     
29169     renderProgressDialog : function()
29170     {
29171         var _this = this;
29172         
29173         this.progressDialog = new Roo.bootstrap.Modal({
29174             cls : 'roo-document-manager-progress-dialog',
29175             allow_close : false,
29176             title : '',
29177             buttons : [
29178                 {
29179                     name  :'cancel',
29180                     weight : 'danger',
29181                     html : 'Cancel'
29182                 }
29183             ], 
29184             listeners : { 
29185                 btnclick : function() {
29186                     _this.uploadCancel();
29187                     this.hide();
29188                 }
29189             }
29190         });
29191          
29192         this.progressDialog.render(Roo.get(document.body));
29193          
29194         this.progress = new Roo.bootstrap.Progress({
29195             cls : 'roo-document-manager-progress',
29196             active : true,
29197             striped : true
29198         });
29199         
29200         this.progress.render(this.progressDialog.getChildContainer());
29201         
29202         this.progressBar = new Roo.bootstrap.ProgressBar({
29203             cls : 'roo-document-manager-progress-bar',
29204             aria_valuenow : 0,
29205             aria_valuemin : 0,
29206             aria_valuemax : 12,
29207             panel : 'success'
29208         });
29209         
29210         this.progressBar.render(this.progress.getChildContainer());
29211     },
29212     
29213     onUploaderClick : function(e)
29214     {
29215         e.preventDefault();
29216      
29217         if(this.fireEvent('beforeselectfile', this) != false){
29218             this.selectorEl.dom.click();
29219         }
29220         
29221     },
29222     
29223     onFileSelected : function(e)
29224     {
29225         e.preventDefault();
29226         
29227         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29228             return;
29229         }
29230         
29231         Roo.each(this.selectorEl.dom.files, function(file){
29232             if(this.fireEvent('inspect', this, file) != false){
29233                 this.files.push(file);
29234             }
29235         }, this);
29236         
29237         this.queue();
29238         
29239     },
29240     
29241     queue : function()
29242     {
29243         this.selectorEl.dom.value = '';
29244         
29245         if(!this.files || !this.files.length){
29246             return;
29247         }
29248         
29249         if(this.boxes > 0 && this.files.length > this.boxes){
29250             this.files = this.files.slice(0, this.boxes);
29251         }
29252         
29253         this.uploader.show();
29254         
29255         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29256             this.uploader.hide();
29257         }
29258         
29259         var _this = this;
29260         
29261         var files = [];
29262         
29263         var docs = [];
29264         
29265         Roo.each(this.files, function(file){
29266             
29267             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29268                 var f = this.renderPreview(file);
29269                 files.push(f);
29270                 return;
29271             }
29272             
29273             if(file.type.indexOf('image') != -1){
29274                 this.delegates.push(
29275                     (function(){
29276                         _this.process(file);
29277                     }).createDelegate(this)
29278                 );
29279         
29280                 return;
29281             }
29282             
29283             docs.push(
29284                 (function(){
29285                     _this.process(file);
29286                 }).createDelegate(this)
29287             );
29288             
29289         }, this);
29290         
29291         this.files = files;
29292         
29293         this.delegates = this.delegates.concat(docs);
29294         
29295         if(!this.delegates.length){
29296             this.refresh();
29297             return;
29298         }
29299         
29300         this.progressBar.aria_valuemax = this.delegates.length;
29301         
29302         this.arrange();
29303         
29304         return;
29305     },
29306     
29307     arrange : function()
29308     {
29309         if(!this.delegates.length){
29310             this.progressDialog.hide();
29311             this.refresh();
29312             return;
29313         }
29314         
29315         var delegate = this.delegates.shift();
29316         
29317         this.progressDialog.show();
29318         
29319         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29320         
29321         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29322         
29323         delegate();
29324     },
29325     
29326     refresh : function()
29327     {
29328         this.uploader.show();
29329         
29330         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29331             this.uploader.hide();
29332         }
29333         
29334         Roo.isTouch ? this.closable(false) : this.closable(true);
29335         
29336         this.fireEvent('refresh', this);
29337     },
29338     
29339     onRemove : function(e, el, o)
29340     {
29341         e.preventDefault();
29342         
29343         this.fireEvent('remove', this, o);
29344         
29345     },
29346     
29347     remove : function(o)
29348     {
29349         var files = [];
29350         
29351         Roo.each(this.files, function(file){
29352             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29353                 files.push(file);
29354                 return;
29355             }
29356
29357             o.target.remove();
29358
29359         }, this);
29360         
29361         this.files = files;
29362         
29363         this.refresh();
29364     },
29365     
29366     clear : function()
29367     {
29368         Roo.each(this.files, function(file){
29369             if(!file.target){
29370                 return;
29371             }
29372             
29373             file.target.remove();
29374
29375         }, this);
29376         
29377         this.files = [];
29378         
29379         this.refresh();
29380     },
29381     
29382     onClick : function(e, el, o)
29383     {
29384         e.preventDefault();
29385         
29386         this.fireEvent('click', this, o);
29387         
29388     },
29389     
29390     closable : function(closable)
29391     {
29392         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29393             
29394             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29395             
29396             if(closable){
29397                 el.show();
29398                 return;
29399             }
29400             
29401             el.hide();
29402             
29403         }, this);
29404     },
29405     
29406     xhrOnLoad : function(xhr)
29407     {
29408         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29409             el.remove();
29410         }, this);
29411         
29412         if (xhr.readyState !== 4) {
29413             this.arrange();
29414             this.fireEvent('exception', this, xhr);
29415             return;
29416         }
29417
29418         var response = Roo.decode(xhr.responseText);
29419         
29420         if(!response.success){
29421             this.arrange();
29422             this.fireEvent('exception', this, xhr);
29423             return;
29424         }
29425         
29426         var file = this.renderPreview(response.data);
29427         
29428         this.files.push(file);
29429         
29430         this.arrange();
29431         
29432         this.fireEvent('afterupload', this, xhr);
29433         
29434     },
29435     
29436     xhrOnError : function(xhr)
29437     {
29438         Roo.log('xhr on error');
29439         
29440         var response = Roo.decode(xhr.responseText);
29441           
29442         Roo.log(response);
29443         
29444         this.arrange();
29445     },
29446     
29447     process : function(file)
29448     {
29449         if(this.fireEvent('process', this, file) !== false){
29450             if(this.editable && file.type.indexOf('image') != -1){
29451                 this.fireEvent('edit', this, file);
29452                 return;
29453             }
29454
29455             this.uploadStart(file, false);
29456
29457             return;
29458         }
29459         
29460     },
29461     
29462     uploadStart : function(file, crop)
29463     {
29464         this.xhr = new XMLHttpRequest();
29465         
29466         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29467             this.arrange();
29468             return;
29469         }
29470         
29471         file.xhr = this.xhr;
29472             
29473         this.managerEl.createChild({
29474             tag : 'div',
29475             cls : 'roo-document-manager-loading',
29476             cn : [
29477                 {
29478                     tag : 'div',
29479                     tooltip : file.name,
29480                     cls : 'roo-document-manager-thumb',
29481                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29482                 }
29483             ]
29484
29485         });
29486
29487         this.xhr.open(this.method, this.url, true);
29488         
29489         var headers = {
29490             "Accept": "application/json",
29491             "Cache-Control": "no-cache",
29492             "X-Requested-With": "XMLHttpRequest"
29493         };
29494         
29495         for (var headerName in headers) {
29496             var headerValue = headers[headerName];
29497             if (headerValue) {
29498                 this.xhr.setRequestHeader(headerName, headerValue);
29499             }
29500         }
29501         
29502         var _this = this;
29503         
29504         this.xhr.onload = function()
29505         {
29506             _this.xhrOnLoad(_this.xhr);
29507         }
29508         
29509         this.xhr.onerror = function()
29510         {
29511             _this.xhrOnError(_this.xhr);
29512         }
29513         
29514         var formData = new FormData();
29515
29516         formData.append('returnHTML', 'NO');
29517         
29518         if(crop){
29519             formData.append('crop', crop);
29520         }
29521         
29522         formData.append(this.paramName, file, file.name);
29523         
29524         var options = {
29525             file : file, 
29526             manually : false
29527         };
29528         
29529         if(this.fireEvent('prepare', this, formData, options) != false){
29530             
29531             if(options.manually){
29532                 return;
29533             }
29534             
29535             this.xhr.send(formData);
29536             return;
29537         };
29538         
29539         this.uploadCancel();
29540     },
29541     
29542     uploadCancel : function()
29543     {
29544         if (this.xhr) {
29545             this.xhr.abort();
29546         }
29547         
29548         this.delegates = [];
29549         
29550         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29551             el.remove();
29552         }, this);
29553         
29554         this.arrange();
29555     },
29556     
29557     renderPreview : function(file)
29558     {
29559         if(typeof(file.target) != 'undefined' && file.target){
29560             return file;
29561         }
29562         
29563         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29564         
29565         var previewEl = this.managerEl.createChild({
29566             tag : 'div',
29567             cls : 'roo-document-manager-preview',
29568             cn : [
29569                 {
29570                     tag : 'div',
29571                     tooltip : file[this.toolTipName],
29572                     cls : 'roo-document-manager-thumb',
29573                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29574                 },
29575                 {
29576                     tag : 'button',
29577                     cls : 'close',
29578                     html : '<i class="fa fa-times-circle"></i>'
29579                 }
29580             ]
29581         });
29582
29583         var close = previewEl.select('button.close', true).first();
29584
29585         close.on('click', this.onRemove, this, file);
29586
29587         file.target = previewEl;
29588
29589         var image = previewEl.select('img', true).first();
29590         
29591         var _this = this;
29592         
29593         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29594         
29595         image.on('click', this.onClick, this, file);
29596         
29597         this.fireEvent('previewrendered', this, file);
29598         
29599         return file;
29600         
29601     },
29602     
29603     onPreviewLoad : function(file, image)
29604     {
29605         if(typeof(file.target) == 'undefined' || !file.target){
29606             return;
29607         }
29608         
29609         var width = image.dom.naturalWidth || image.dom.width;
29610         var height = image.dom.naturalHeight || image.dom.height;
29611         
29612         if(!this.previewResize) {
29613             return;
29614         }
29615         
29616         if(width > height){
29617             file.target.addClass('wide');
29618             return;
29619         }
29620         
29621         file.target.addClass('tall');
29622         return;
29623         
29624     },
29625     
29626     uploadFromSource : function(file, crop)
29627     {
29628         this.xhr = new XMLHttpRequest();
29629         
29630         this.managerEl.createChild({
29631             tag : 'div',
29632             cls : 'roo-document-manager-loading',
29633             cn : [
29634                 {
29635                     tag : 'div',
29636                     tooltip : file.name,
29637                     cls : 'roo-document-manager-thumb',
29638                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29639                 }
29640             ]
29641
29642         });
29643
29644         this.xhr.open(this.method, this.url, true);
29645         
29646         var headers = {
29647             "Accept": "application/json",
29648             "Cache-Control": "no-cache",
29649             "X-Requested-With": "XMLHttpRequest"
29650         };
29651         
29652         for (var headerName in headers) {
29653             var headerValue = headers[headerName];
29654             if (headerValue) {
29655                 this.xhr.setRequestHeader(headerName, headerValue);
29656             }
29657         }
29658         
29659         var _this = this;
29660         
29661         this.xhr.onload = function()
29662         {
29663             _this.xhrOnLoad(_this.xhr);
29664         }
29665         
29666         this.xhr.onerror = function()
29667         {
29668             _this.xhrOnError(_this.xhr);
29669         }
29670         
29671         var formData = new FormData();
29672
29673         formData.append('returnHTML', 'NO');
29674         
29675         formData.append('crop', crop);
29676         
29677         if(typeof(file.filename) != 'undefined'){
29678             formData.append('filename', file.filename);
29679         }
29680         
29681         if(typeof(file.mimetype) != 'undefined'){
29682             formData.append('mimetype', file.mimetype);
29683         }
29684         
29685         Roo.log(formData);
29686         
29687         if(this.fireEvent('prepare', this, formData) != false){
29688             this.xhr.send(formData);
29689         };
29690     }
29691 });
29692
29693 /*
29694 * Licence: LGPL
29695 */
29696
29697 /**
29698  * @class Roo.bootstrap.DocumentViewer
29699  * @extends Roo.bootstrap.Component
29700  * Bootstrap DocumentViewer class
29701  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29702  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29703  * 
29704  * @constructor
29705  * Create a new DocumentViewer
29706  * @param {Object} config The config object
29707  */
29708
29709 Roo.bootstrap.DocumentViewer = function(config){
29710     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29711     
29712     this.addEvents({
29713         /**
29714          * @event initial
29715          * Fire after initEvent
29716          * @param {Roo.bootstrap.DocumentViewer} this
29717          */
29718         "initial" : true,
29719         /**
29720          * @event click
29721          * Fire after click
29722          * @param {Roo.bootstrap.DocumentViewer} this
29723          */
29724         "click" : true,
29725         /**
29726          * @event download
29727          * Fire after download button
29728          * @param {Roo.bootstrap.DocumentViewer} this
29729          */
29730         "download" : true,
29731         /**
29732          * @event trash
29733          * Fire after trash button
29734          * @param {Roo.bootstrap.DocumentViewer} this
29735          */
29736         "trash" : true
29737         
29738     });
29739 };
29740
29741 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29742     
29743     showDownload : true,
29744     
29745     showTrash : true,
29746     
29747     getAutoCreate : function()
29748     {
29749         var cfg = {
29750             tag : 'div',
29751             cls : 'roo-document-viewer',
29752             cn : [
29753                 {
29754                     tag : 'div',
29755                     cls : 'roo-document-viewer-body',
29756                     cn : [
29757                         {
29758                             tag : 'div',
29759                             cls : 'roo-document-viewer-thumb',
29760                             cn : [
29761                                 {
29762                                     tag : 'img',
29763                                     cls : 'roo-document-viewer-image'
29764                                 }
29765                             ]
29766                         }
29767                     ]
29768                 },
29769                 {
29770                     tag : 'div',
29771                     cls : 'roo-document-viewer-footer',
29772                     cn : {
29773                         tag : 'div',
29774                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29775                         cn : [
29776                             {
29777                                 tag : 'div',
29778                                 cls : 'btn-group roo-document-viewer-download',
29779                                 cn : [
29780                                     {
29781                                         tag : 'button',
29782                                         cls : 'btn btn-default',
29783                                         html : '<i class="fa fa-download"></i>'
29784                                     }
29785                                 ]
29786                             },
29787                             {
29788                                 tag : 'div',
29789                                 cls : 'btn-group roo-document-viewer-trash',
29790                                 cn : [
29791                                     {
29792                                         tag : 'button',
29793                                         cls : 'btn btn-default',
29794                                         html : '<i class="fa fa-trash"></i>'
29795                                     }
29796                                 ]
29797                             }
29798                         ]
29799                     }
29800                 }
29801             ]
29802         };
29803         
29804         return cfg;
29805     },
29806     
29807     initEvents : function()
29808     {
29809         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29810         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29811         
29812         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29813         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29814         
29815         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29816         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29817         
29818         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29819         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29820         
29821         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29822         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29823         
29824         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29825         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29826         
29827         this.bodyEl.on('click', this.onClick, this);
29828         this.downloadBtn.on('click', this.onDownload, this);
29829         this.trashBtn.on('click', this.onTrash, this);
29830         
29831         this.downloadBtn.hide();
29832         this.trashBtn.hide();
29833         
29834         if(this.showDownload){
29835             this.downloadBtn.show();
29836         }
29837         
29838         if(this.showTrash){
29839             this.trashBtn.show();
29840         }
29841         
29842         if(!this.showDownload && !this.showTrash) {
29843             this.footerEl.hide();
29844         }
29845         
29846     },
29847     
29848     initial : function()
29849     {
29850         this.fireEvent('initial', this);
29851         
29852     },
29853     
29854     onClick : function(e)
29855     {
29856         e.preventDefault();
29857         
29858         this.fireEvent('click', this);
29859     },
29860     
29861     onDownload : function(e)
29862     {
29863         e.preventDefault();
29864         
29865         this.fireEvent('download', this);
29866     },
29867     
29868     onTrash : function(e)
29869     {
29870         e.preventDefault();
29871         
29872         this.fireEvent('trash', this);
29873     }
29874     
29875 });
29876 /*
29877  * - LGPL
29878  *
29879  * nav progress bar
29880  * 
29881  */
29882
29883 /**
29884  * @class Roo.bootstrap.NavProgressBar
29885  * @extends Roo.bootstrap.Component
29886  * Bootstrap NavProgressBar class
29887  * 
29888  * @constructor
29889  * Create a new nav progress bar
29890  * @param {Object} config The config object
29891  */
29892
29893 Roo.bootstrap.NavProgressBar = function(config){
29894     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29895
29896     this.bullets = this.bullets || [];
29897    
29898 //    Roo.bootstrap.NavProgressBar.register(this);
29899      this.addEvents({
29900         /**
29901              * @event changed
29902              * Fires when the active item changes
29903              * @param {Roo.bootstrap.NavProgressBar} this
29904              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29905              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29906          */
29907         'changed': true
29908      });
29909     
29910 };
29911
29912 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29913     
29914     bullets : [],
29915     barItems : [],
29916     
29917     getAutoCreate : function()
29918     {
29919         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29920         
29921         cfg = {
29922             tag : 'div',
29923             cls : 'roo-navigation-bar-group',
29924             cn : [
29925                 {
29926                     tag : 'div',
29927                     cls : 'roo-navigation-top-bar'
29928                 },
29929                 {
29930                     tag : 'div',
29931                     cls : 'roo-navigation-bullets-bar',
29932                     cn : [
29933                         {
29934                             tag : 'ul',
29935                             cls : 'roo-navigation-bar'
29936                         }
29937                     ]
29938                 },
29939                 
29940                 {
29941                     tag : 'div',
29942                     cls : 'roo-navigation-bottom-bar'
29943                 }
29944             ]
29945             
29946         };
29947         
29948         return cfg;
29949         
29950     },
29951     
29952     initEvents: function() 
29953     {
29954         
29955     },
29956     
29957     onRender : function(ct, position) 
29958     {
29959         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29960         
29961         if(this.bullets.length){
29962             Roo.each(this.bullets, function(b){
29963                this.addItem(b);
29964             }, this);
29965         }
29966         
29967         this.format();
29968         
29969     },
29970     
29971     addItem : function(cfg)
29972     {
29973         var item = new Roo.bootstrap.NavProgressItem(cfg);
29974         
29975         item.parentId = this.id;
29976         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29977         
29978         if(cfg.html){
29979             var top = new Roo.bootstrap.Element({
29980                 tag : 'div',
29981                 cls : 'roo-navigation-bar-text'
29982             });
29983             
29984             var bottom = new Roo.bootstrap.Element({
29985                 tag : 'div',
29986                 cls : 'roo-navigation-bar-text'
29987             });
29988             
29989             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29990             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29991             
29992             var topText = new Roo.bootstrap.Element({
29993                 tag : 'span',
29994                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29995             });
29996             
29997             var bottomText = new Roo.bootstrap.Element({
29998                 tag : 'span',
29999                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30000             });
30001             
30002             topText.onRender(top.el, null);
30003             bottomText.onRender(bottom.el, null);
30004             
30005             item.topEl = top;
30006             item.bottomEl = bottom;
30007         }
30008         
30009         this.barItems.push(item);
30010         
30011         return item;
30012     },
30013     
30014     getActive : function()
30015     {
30016         var active = false;
30017         
30018         Roo.each(this.barItems, function(v){
30019             
30020             if (!v.isActive()) {
30021                 return;
30022             }
30023             
30024             active = v;
30025             return false;
30026             
30027         });
30028         
30029         return active;
30030     },
30031     
30032     setActiveItem : function(item)
30033     {
30034         var prev = false;
30035         
30036         Roo.each(this.barItems, function(v){
30037             if (v.rid == item.rid) {
30038                 return ;
30039             }
30040             
30041             if (v.isActive()) {
30042                 v.setActive(false);
30043                 prev = v;
30044             }
30045         });
30046
30047         item.setActive(true);
30048         
30049         this.fireEvent('changed', this, item, prev);
30050     },
30051     
30052     getBarItem: function(rid)
30053     {
30054         var ret = false;
30055         
30056         Roo.each(this.barItems, function(e) {
30057             if (e.rid != rid) {
30058                 return;
30059             }
30060             
30061             ret =  e;
30062             return false;
30063         });
30064         
30065         return ret;
30066     },
30067     
30068     indexOfItem : function(item)
30069     {
30070         var index = false;
30071         
30072         Roo.each(this.barItems, function(v, i){
30073             
30074             if (v.rid != item.rid) {
30075                 return;
30076             }
30077             
30078             index = i;
30079             return false
30080         });
30081         
30082         return index;
30083     },
30084     
30085     setActiveNext : function()
30086     {
30087         var i = this.indexOfItem(this.getActive());
30088         
30089         if (i > this.barItems.length) {
30090             return;
30091         }
30092         
30093         this.setActiveItem(this.barItems[i+1]);
30094     },
30095     
30096     setActivePrev : function()
30097     {
30098         var i = this.indexOfItem(this.getActive());
30099         
30100         if (i  < 1) {
30101             return;
30102         }
30103         
30104         this.setActiveItem(this.barItems[i-1]);
30105     },
30106     
30107     format : function()
30108     {
30109         if(!this.barItems.length){
30110             return;
30111         }
30112      
30113         var width = 100 / this.barItems.length;
30114         
30115         Roo.each(this.barItems, function(i){
30116             i.el.setStyle('width', width + '%');
30117             i.topEl.el.setStyle('width', width + '%');
30118             i.bottomEl.el.setStyle('width', width + '%');
30119         }, this);
30120         
30121     }
30122     
30123 });
30124 /*
30125  * - LGPL
30126  *
30127  * Nav Progress Item
30128  * 
30129  */
30130
30131 /**
30132  * @class Roo.bootstrap.NavProgressItem
30133  * @extends Roo.bootstrap.Component
30134  * Bootstrap NavProgressItem class
30135  * @cfg {String} rid the reference id
30136  * @cfg {Boolean} active (true|false) Is item active default false
30137  * @cfg {Boolean} disabled (true|false) Is item active default false
30138  * @cfg {String} html
30139  * @cfg {String} position (top|bottom) text position default bottom
30140  * @cfg {String} icon show icon instead of number
30141  * 
30142  * @constructor
30143  * Create a new NavProgressItem
30144  * @param {Object} config The config object
30145  */
30146 Roo.bootstrap.NavProgressItem = function(config){
30147     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30148     this.addEvents({
30149         // raw events
30150         /**
30151          * @event click
30152          * The raw click event for the entire grid.
30153          * @param {Roo.bootstrap.NavProgressItem} this
30154          * @param {Roo.EventObject} e
30155          */
30156         "click" : true
30157     });
30158    
30159 };
30160
30161 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30162     
30163     rid : '',
30164     active : false,
30165     disabled : false,
30166     html : '',
30167     position : 'bottom',
30168     icon : false,
30169     
30170     getAutoCreate : function()
30171     {
30172         var iconCls = 'roo-navigation-bar-item-icon';
30173         
30174         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30175         
30176         var cfg = {
30177             tag: 'li',
30178             cls: 'roo-navigation-bar-item',
30179             cn : [
30180                 {
30181                     tag : 'i',
30182                     cls : iconCls
30183                 }
30184             ]
30185         };
30186         
30187         if(this.active){
30188             cfg.cls += ' active';
30189         }
30190         if(this.disabled){
30191             cfg.cls += ' disabled';
30192         }
30193         
30194         return cfg;
30195     },
30196     
30197     disable : function()
30198     {
30199         this.setDisabled(true);
30200     },
30201     
30202     enable : function()
30203     {
30204         this.setDisabled(false);
30205     },
30206     
30207     initEvents: function() 
30208     {
30209         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30210         
30211         this.iconEl.on('click', this.onClick, this);
30212     },
30213     
30214     onClick : function(e)
30215     {
30216         e.preventDefault();
30217         
30218         if(this.disabled){
30219             return;
30220         }
30221         
30222         if(this.fireEvent('click', this, e) === false){
30223             return;
30224         };
30225         
30226         this.parent().setActiveItem(this);
30227     },
30228     
30229     isActive: function () 
30230     {
30231         return this.active;
30232     },
30233     
30234     setActive : function(state)
30235     {
30236         if(this.active == state){
30237             return;
30238         }
30239         
30240         this.active = state;
30241         
30242         if (state) {
30243             this.el.addClass('active');
30244             return;
30245         }
30246         
30247         this.el.removeClass('active');
30248         
30249         return;
30250     },
30251     
30252     setDisabled : function(state)
30253     {
30254         if(this.disabled == state){
30255             return;
30256         }
30257         
30258         this.disabled = state;
30259         
30260         if (state) {
30261             this.el.addClass('disabled');
30262             return;
30263         }
30264         
30265         this.el.removeClass('disabled');
30266     },
30267     
30268     tooltipEl : function()
30269     {
30270         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30271     }
30272 });
30273  
30274
30275  /*
30276  * - LGPL
30277  *
30278  * FieldLabel
30279  * 
30280  */
30281
30282 /**
30283  * @class Roo.bootstrap.FieldLabel
30284  * @extends Roo.bootstrap.Component
30285  * Bootstrap FieldLabel class
30286  * @cfg {String} html contents of the element
30287  * @cfg {String} tag tag of the element default label
30288  * @cfg {String} cls class of the element
30289  * @cfg {String} target label target 
30290  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30291  * @cfg {String} invalidClass default "text-warning"
30292  * @cfg {String} validClass default "text-success"
30293  * @cfg {String} iconTooltip default "This field is required"
30294  * @cfg {String} indicatorpos (left|right) default left
30295  * 
30296  * @constructor
30297  * Create a new FieldLabel
30298  * @param {Object} config The config object
30299  */
30300
30301 Roo.bootstrap.FieldLabel = function(config){
30302     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30303     
30304     this.addEvents({
30305             /**
30306              * @event invalid
30307              * Fires after the field has been marked as invalid.
30308              * @param {Roo.form.FieldLabel} this
30309              * @param {String} msg The validation message
30310              */
30311             invalid : true,
30312             /**
30313              * @event valid
30314              * Fires after the field has been validated with no errors.
30315              * @param {Roo.form.FieldLabel} this
30316              */
30317             valid : true
30318         });
30319 };
30320
30321 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30322     
30323     tag: 'label',
30324     cls: '',
30325     html: '',
30326     target: '',
30327     allowBlank : true,
30328     invalidClass : 'has-warning',
30329     validClass : 'has-success',
30330     iconTooltip : 'This field is required',
30331     indicatorpos : 'left',
30332     
30333     getAutoCreate : function(){
30334         
30335         var cls = "";
30336         if (!this.allowBlank) {
30337             cls  = "visible";
30338         }
30339         
30340         var cfg = {
30341             tag : this.tag,
30342             cls : 'roo-bootstrap-field-label ' + this.cls,
30343             for : this.target,
30344             cn : [
30345                 {
30346                     tag : 'i',
30347                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30348                     tooltip : this.iconTooltip
30349                 },
30350                 {
30351                     tag : 'span',
30352                     html : this.html
30353                 }
30354             ] 
30355         };
30356         
30357         if(this.indicatorpos == 'right'){
30358             var cfg = {
30359                 tag : this.tag,
30360                 cls : 'roo-bootstrap-field-label ' + this.cls,
30361                 for : this.target,
30362                 cn : [
30363                     {
30364                         tag : 'span',
30365                         html : this.html
30366                     },
30367                     {
30368                         tag : 'i',
30369                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30370                         tooltip : this.iconTooltip
30371                     }
30372                 ] 
30373             };
30374         }
30375         
30376         return cfg;
30377     },
30378     
30379     initEvents: function() 
30380     {
30381         Roo.bootstrap.Element.superclass.initEvents.call(this);
30382         
30383         this.indicator = this.indicatorEl();
30384         
30385         if(this.indicator){
30386             this.indicator.removeClass('visible');
30387             this.indicator.addClass('invisible');
30388         }
30389         
30390         Roo.bootstrap.FieldLabel.register(this);
30391     },
30392     
30393     indicatorEl : function()
30394     {
30395         var indicator = this.el.select('i.roo-required-indicator',true).first();
30396         
30397         if(!indicator){
30398             return false;
30399         }
30400         
30401         return indicator;
30402         
30403     },
30404     
30405     /**
30406      * Mark this field as valid
30407      */
30408     markValid : function()
30409     {
30410         if(this.indicator){
30411             this.indicator.removeClass('visible');
30412             this.indicator.addClass('invisible');
30413         }
30414         
30415         this.el.removeClass(this.invalidClass);
30416         
30417         this.el.addClass(this.validClass);
30418         
30419         this.fireEvent('valid', this);
30420     },
30421     
30422     /**
30423      * Mark this field as invalid
30424      * @param {String} msg The validation message
30425      */
30426     markInvalid : function(msg)
30427     {
30428         if(this.indicator){
30429             this.indicator.removeClass('invisible');
30430             this.indicator.addClass('visible');
30431         }
30432         
30433         this.el.removeClass(this.validClass);
30434         
30435         this.el.addClass(this.invalidClass);
30436         
30437         this.fireEvent('invalid', this, msg);
30438     }
30439     
30440    
30441 });
30442
30443 Roo.apply(Roo.bootstrap.FieldLabel, {
30444     
30445     groups: {},
30446     
30447      /**
30448     * register a FieldLabel Group
30449     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30450     */
30451     register : function(label)
30452     {
30453         if(this.groups.hasOwnProperty(label.target)){
30454             return;
30455         }
30456      
30457         this.groups[label.target] = label;
30458         
30459     },
30460     /**
30461     * fetch a FieldLabel Group based on the target
30462     * @param {string} target
30463     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30464     */
30465     get: function(target) {
30466         if (typeof(this.groups[target]) == 'undefined') {
30467             return false;
30468         }
30469         
30470         return this.groups[target] ;
30471     }
30472 });
30473
30474  
30475
30476  /*
30477  * - LGPL
30478  *
30479  * page DateSplitField.
30480  * 
30481  */
30482
30483
30484 /**
30485  * @class Roo.bootstrap.DateSplitField
30486  * @extends Roo.bootstrap.Component
30487  * Bootstrap DateSplitField class
30488  * @cfg {string} fieldLabel - the label associated
30489  * @cfg {Number} labelWidth set the width of label (0-12)
30490  * @cfg {String} labelAlign (top|left)
30491  * @cfg {Boolean} dayAllowBlank (true|false) default false
30492  * @cfg {Boolean} monthAllowBlank (true|false) default false
30493  * @cfg {Boolean} yearAllowBlank (true|false) default false
30494  * @cfg {string} dayPlaceholder 
30495  * @cfg {string} monthPlaceholder
30496  * @cfg {string} yearPlaceholder
30497  * @cfg {string} dayFormat default 'd'
30498  * @cfg {string} monthFormat default 'm'
30499  * @cfg {string} yearFormat default 'Y'
30500  * @cfg {Number} labellg set the width of label (1-12)
30501  * @cfg {Number} labelmd set the width of label (1-12)
30502  * @cfg {Number} labelsm set the width of label (1-12)
30503  * @cfg {Number} labelxs set the width of label (1-12)
30504
30505  *     
30506  * @constructor
30507  * Create a new DateSplitField
30508  * @param {Object} config The config object
30509  */
30510
30511 Roo.bootstrap.DateSplitField = function(config){
30512     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30513     
30514     this.addEvents({
30515         // raw events
30516          /**
30517          * @event years
30518          * getting the data of years
30519          * @param {Roo.bootstrap.DateSplitField} this
30520          * @param {Object} years
30521          */
30522         "years" : true,
30523         /**
30524          * @event days
30525          * getting the data of days
30526          * @param {Roo.bootstrap.DateSplitField} this
30527          * @param {Object} days
30528          */
30529         "days" : true,
30530         /**
30531          * @event invalid
30532          * Fires after the field has been marked as invalid.
30533          * @param {Roo.form.Field} this
30534          * @param {String} msg The validation message
30535          */
30536         invalid : true,
30537        /**
30538          * @event valid
30539          * Fires after the field has been validated with no errors.
30540          * @param {Roo.form.Field} this
30541          */
30542         valid : true
30543     });
30544 };
30545
30546 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30547     
30548     fieldLabel : '',
30549     labelAlign : 'top',
30550     labelWidth : 3,
30551     dayAllowBlank : false,
30552     monthAllowBlank : false,
30553     yearAllowBlank : false,
30554     dayPlaceholder : '',
30555     monthPlaceholder : '',
30556     yearPlaceholder : '',
30557     dayFormat : 'd',
30558     monthFormat : 'm',
30559     yearFormat : 'Y',
30560     isFormField : true,
30561     labellg : 0,
30562     labelmd : 0,
30563     labelsm : 0,
30564     labelxs : 0,
30565     
30566     getAutoCreate : function()
30567     {
30568         var cfg = {
30569             tag : 'div',
30570             cls : 'row roo-date-split-field-group',
30571             cn : [
30572                 {
30573                     tag : 'input',
30574                     type : 'hidden',
30575                     cls : 'form-hidden-field roo-date-split-field-group-value',
30576                     name : this.name
30577                 }
30578             ]
30579         };
30580         
30581         var labelCls = 'col-md-12';
30582         var contentCls = 'col-md-4';
30583         
30584         if(this.fieldLabel){
30585             
30586             var label = {
30587                 tag : 'div',
30588                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30589                 cn : [
30590                     {
30591                         tag : 'label',
30592                         html : this.fieldLabel
30593                     }
30594                 ]
30595             };
30596             
30597             if(this.labelAlign == 'left'){
30598             
30599                 if(this.labelWidth > 12){
30600                     label.style = "width: " + this.labelWidth + 'px';
30601                 }
30602
30603                 if(this.labelWidth < 13 && this.labelmd == 0){
30604                     this.labelmd = this.labelWidth;
30605                 }
30606
30607                 if(this.labellg > 0){
30608                     labelCls = ' col-lg-' + this.labellg;
30609                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30610                 }
30611
30612                 if(this.labelmd > 0){
30613                     labelCls = ' col-md-' + this.labelmd;
30614                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30615                 }
30616
30617                 if(this.labelsm > 0){
30618                     labelCls = ' col-sm-' + this.labelsm;
30619                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30620                 }
30621
30622                 if(this.labelxs > 0){
30623                     labelCls = ' col-xs-' + this.labelxs;
30624                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30625                 }
30626             }
30627             
30628             label.cls += ' ' + labelCls;
30629             
30630             cfg.cn.push(label);
30631         }
30632         
30633         Roo.each(['day', 'month', 'year'], function(t){
30634             cfg.cn.push({
30635                 tag : 'div',
30636                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30637             });
30638         }, this);
30639         
30640         return cfg;
30641     },
30642     
30643     inputEl: function ()
30644     {
30645         return this.el.select('.roo-date-split-field-group-value', true).first();
30646     },
30647     
30648     onRender : function(ct, position) 
30649     {
30650         var _this = this;
30651         
30652         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30653         
30654         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30655         
30656         this.dayField = new Roo.bootstrap.ComboBox({
30657             allowBlank : this.dayAllowBlank,
30658             alwaysQuery : true,
30659             displayField : 'value',
30660             editable : false,
30661             fieldLabel : '',
30662             forceSelection : true,
30663             mode : 'local',
30664             placeholder : this.dayPlaceholder,
30665             selectOnFocus : true,
30666             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30667             triggerAction : 'all',
30668             typeAhead : true,
30669             valueField : 'value',
30670             store : new Roo.data.SimpleStore({
30671                 data : (function() {    
30672                     var days = [];
30673                     _this.fireEvent('days', _this, days);
30674                     return days;
30675                 })(),
30676                 fields : [ 'value' ]
30677             }),
30678             listeners : {
30679                 select : function (_self, record, index)
30680                 {
30681                     _this.setValue(_this.getValue());
30682                 }
30683             }
30684         });
30685
30686         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30687         
30688         this.monthField = new Roo.bootstrap.MonthField({
30689             after : '<i class=\"fa fa-calendar\"></i>',
30690             allowBlank : this.monthAllowBlank,
30691             placeholder : this.monthPlaceholder,
30692             readOnly : true,
30693             listeners : {
30694                 render : function (_self)
30695                 {
30696                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30697                         e.preventDefault();
30698                         _self.focus();
30699                     });
30700                 },
30701                 select : function (_self, oldvalue, newvalue)
30702                 {
30703                     _this.setValue(_this.getValue());
30704                 }
30705             }
30706         });
30707         
30708         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30709         
30710         this.yearField = new Roo.bootstrap.ComboBox({
30711             allowBlank : this.yearAllowBlank,
30712             alwaysQuery : true,
30713             displayField : 'value',
30714             editable : false,
30715             fieldLabel : '',
30716             forceSelection : true,
30717             mode : 'local',
30718             placeholder : this.yearPlaceholder,
30719             selectOnFocus : true,
30720             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30721             triggerAction : 'all',
30722             typeAhead : true,
30723             valueField : 'value',
30724             store : new Roo.data.SimpleStore({
30725                 data : (function() {
30726                     var years = [];
30727                     _this.fireEvent('years', _this, years);
30728                     return years;
30729                 })(),
30730                 fields : [ 'value' ]
30731             }),
30732             listeners : {
30733                 select : function (_self, record, index)
30734                 {
30735                     _this.setValue(_this.getValue());
30736                 }
30737             }
30738         });
30739
30740         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30741     },
30742     
30743     setValue : function(v, format)
30744     {
30745         this.inputEl.dom.value = v;
30746         
30747         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30748         
30749         var d = Date.parseDate(v, f);
30750         
30751         if(!d){
30752             this.validate();
30753             return;
30754         }
30755         
30756         this.setDay(d.format(this.dayFormat));
30757         this.setMonth(d.format(this.monthFormat));
30758         this.setYear(d.format(this.yearFormat));
30759         
30760         this.validate();
30761         
30762         return;
30763     },
30764     
30765     setDay : function(v)
30766     {
30767         this.dayField.setValue(v);
30768         this.inputEl.dom.value = this.getValue();
30769         this.validate();
30770         return;
30771     },
30772     
30773     setMonth : function(v)
30774     {
30775         this.monthField.setValue(v, true);
30776         this.inputEl.dom.value = this.getValue();
30777         this.validate();
30778         return;
30779     },
30780     
30781     setYear : function(v)
30782     {
30783         this.yearField.setValue(v);
30784         this.inputEl.dom.value = this.getValue();
30785         this.validate();
30786         return;
30787     },
30788     
30789     getDay : function()
30790     {
30791         return this.dayField.getValue();
30792     },
30793     
30794     getMonth : function()
30795     {
30796         return this.monthField.getValue();
30797     },
30798     
30799     getYear : function()
30800     {
30801         return this.yearField.getValue();
30802     },
30803     
30804     getValue : function()
30805     {
30806         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30807         
30808         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30809         
30810         return date;
30811     },
30812     
30813     reset : function()
30814     {
30815         this.setDay('');
30816         this.setMonth('');
30817         this.setYear('');
30818         this.inputEl.dom.value = '';
30819         this.validate();
30820         return;
30821     },
30822     
30823     validate : function()
30824     {
30825         var d = this.dayField.validate();
30826         var m = this.monthField.validate();
30827         var y = this.yearField.validate();
30828         
30829         var valid = true;
30830         
30831         if(
30832                 (!this.dayAllowBlank && !d) ||
30833                 (!this.monthAllowBlank && !m) ||
30834                 (!this.yearAllowBlank && !y)
30835         ){
30836             valid = false;
30837         }
30838         
30839         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30840             return valid;
30841         }
30842         
30843         if(valid){
30844             this.markValid();
30845             return valid;
30846         }
30847         
30848         this.markInvalid();
30849         
30850         return valid;
30851     },
30852     
30853     markValid : function()
30854     {
30855         
30856         var label = this.el.select('label', true).first();
30857         var icon = this.el.select('i.fa-star', true).first();
30858
30859         if(label && icon){
30860             icon.remove();
30861         }
30862         
30863         this.fireEvent('valid', this);
30864     },
30865     
30866      /**
30867      * Mark this field as invalid
30868      * @param {String} msg The validation message
30869      */
30870     markInvalid : function(msg)
30871     {
30872         
30873         var label = this.el.select('label', true).first();
30874         var icon = this.el.select('i.fa-star', true).first();
30875
30876         if(label && !icon){
30877             this.el.select('.roo-date-split-field-label', true).createChild({
30878                 tag : 'i',
30879                 cls : 'text-danger fa fa-lg fa-star',
30880                 tooltip : 'This field is required',
30881                 style : 'margin-right:5px;'
30882             }, label, true);
30883         }
30884         
30885         this.fireEvent('invalid', this, msg);
30886     },
30887     
30888     clearInvalid : function()
30889     {
30890         var label = this.el.select('label', true).first();
30891         var icon = this.el.select('i.fa-star', true).first();
30892
30893         if(label && icon){
30894             icon.remove();
30895         }
30896         
30897         this.fireEvent('valid', this);
30898     },
30899     
30900     getName: function()
30901     {
30902         return this.name;
30903     }
30904     
30905 });
30906
30907  /**
30908  *
30909  * This is based on 
30910  * http://masonry.desandro.com
30911  *
30912  * The idea is to render all the bricks based on vertical width...
30913  *
30914  * The original code extends 'outlayer' - we might need to use that....
30915  * 
30916  */
30917
30918
30919 /**
30920  * @class Roo.bootstrap.LayoutMasonry
30921  * @extends Roo.bootstrap.Component
30922  * Bootstrap Layout Masonry class
30923  * 
30924  * @constructor
30925  * Create a new Element
30926  * @param {Object} config The config object
30927  */
30928
30929 Roo.bootstrap.LayoutMasonry = function(config){
30930     
30931     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30932     
30933     this.bricks = [];
30934     
30935     Roo.bootstrap.LayoutMasonry.register(this);
30936     
30937     this.addEvents({
30938         // raw events
30939         /**
30940          * @event layout
30941          * Fire after layout the items
30942          * @param {Roo.bootstrap.LayoutMasonry} this
30943          * @param {Roo.EventObject} e
30944          */
30945         "layout" : true
30946     });
30947     
30948 };
30949
30950 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30951     
30952     /**
30953      * @cfg {Boolean} isLayoutInstant = no animation?
30954      */   
30955     isLayoutInstant : false, // needed?
30956    
30957     /**
30958      * @cfg {Number} boxWidth  width of the columns
30959      */   
30960     boxWidth : 450,
30961     
30962       /**
30963      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30964      */   
30965     boxHeight : 0,
30966     
30967     /**
30968      * @cfg {Number} padWidth padding below box..
30969      */   
30970     padWidth : 10, 
30971     
30972     /**
30973      * @cfg {Number} gutter gutter width..
30974      */   
30975     gutter : 10,
30976     
30977      /**
30978      * @cfg {Number} maxCols maximum number of columns
30979      */   
30980     
30981     maxCols: 0,
30982     
30983     /**
30984      * @cfg {Boolean} isAutoInitial defalut true
30985      */   
30986     isAutoInitial : true, 
30987     
30988     containerWidth: 0,
30989     
30990     /**
30991      * @cfg {Boolean} isHorizontal defalut false
30992      */   
30993     isHorizontal : false, 
30994
30995     currentSize : null,
30996     
30997     tag: 'div',
30998     
30999     cls: '',
31000     
31001     bricks: null, //CompositeElement
31002     
31003     cols : 1,
31004     
31005     _isLayoutInited : false,
31006     
31007 //    isAlternative : false, // only use for vertical layout...
31008     
31009     /**
31010      * @cfg {Number} alternativePadWidth padding below box..
31011      */   
31012     alternativePadWidth : 50,
31013     
31014     selectedBrick : [],
31015     
31016     getAutoCreate : function(){
31017         
31018         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31019         
31020         var cfg = {
31021             tag: this.tag,
31022             cls: 'blog-masonary-wrapper ' + this.cls,
31023             cn : {
31024                 cls : 'mas-boxes masonary'
31025             }
31026         };
31027         
31028         return cfg;
31029     },
31030     
31031     getChildContainer: function( )
31032     {
31033         if (this.boxesEl) {
31034             return this.boxesEl;
31035         }
31036         
31037         this.boxesEl = this.el.select('.mas-boxes').first();
31038         
31039         return this.boxesEl;
31040     },
31041     
31042     
31043     initEvents : function()
31044     {
31045         var _this = this;
31046         
31047         if(this.isAutoInitial){
31048             Roo.log('hook children rendered');
31049             this.on('childrenrendered', function() {
31050                 Roo.log('children rendered');
31051                 _this.initial();
31052             } ,this);
31053         }
31054     },
31055     
31056     initial : function()
31057     {
31058         this.selectedBrick = [];
31059         
31060         this.currentSize = this.el.getBox(true);
31061         
31062         Roo.EventManager.onWindowResize(this.resize, this); 
31063
31064         if(!this.isAutoInitial){
31065             this.layout();
31066             return;
31067         }
31068         
31069         this.layout();
31070         
31071         return;
31072         //this.layout.defer(500,this);
31073         
31074     },
31075     
31076     resize : function()
31077     {
31078         var cs = this.el.getBox(true);
31079         
31080         if (
31081                 this.currentSize.width == cs.width && 
31082                 this.currentSize.x == cs.x && 
31083                 this.currentSize.height == cs.height && 
31084                 this.currentSize.y == cs.y 
31085         ) {
31086             Roo.log("no change in with or X or Y");
31087             return;
31088         }
31089         
31090         this.currentSize = cs;
31091         
31092         this.layout();
31093         
31094     },
31095     
31096     layout : function()
31097     {   
31098         this._resetLayout();
31099         
31100         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31101         
31102         this.layoutItems( isInstant );
31103       
31104         this._isLayoutInited = true;
31105         
31106         this.fireEvent('layout', this);
31107         
31108     },
31109     
31110     _resetLayout : function()
31111     {
31112         if(this.isHorizontal){
31113             this.horizontalMeasureColumns();
31114             return;
31115         }
31116         
31117         this.verticalMeasureColumns();
31118         
31119     },
31120     
31121     verticalMeasureColumns : function()
31122     {
31123         this.getContainerWidth();
31124         
31125 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31126 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31127 //            return;
31128 //        }
31129         
31130         var boxWidth = this.boxWidth + this.padWidth;
31131         
31132         if(this.containerWidth < this.boxWidth){
31133             boxWidth = this.containerWidth
31134         }
31135         
31136         var containerWidth = this.containerWidth;
31137         
31138         var cols = Math.floor(containerWidth / boxWidth);
31139         
31140         this.cols = Math.max( cols, 1 );
31141         
31142         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31143         
31144         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31145         
31146         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31147         
31148         this.colWidth = boxWidth + avail - this.padWidth;
31149         
31150         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31151         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31152     },
31153     
31154     horizontalMeasureColumns : function()
31155     {
31156         this.getContainerWidth();
31157         
31158         var boxWidth = this.boxWidth;
31159         
31160         if(this.containerWidth < boxWidth){
31161             boxWidth = this.containerWidth;
31162         }
31163         
31164         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31165         
31166         this.el.setHeight(boxWidth);
31167         
31168     },
31169     
31170     getContainerWidth : function()
31171     {
31172         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31173     },
31174     
31175     layoutItems : function( isInstant )
31176     {
31177         Roo.log(this.bricks);
31178         
31179         var items = Roo.apply([], this.bricks);
31180         
31181         if(this.isHorizontal){
31182             this._horizontalLayoutItems( items , isInstant );
31183             return;
31184         }
31185         
31186 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31187 //            this._verticalAlternativeLayoutItems( items , isInstant );
31188 //            return;
31189 //        }
31190         
31191         this._verticalLayoutItems( items , isInstant );
31192         
31193     },
31194     
31195     _verticalLayoutItems : function ( items , isInstant)
31196     {
31197         if ( !items || !items.length ) {
31198             return;
31199         }
31200         
31201         var standard = [
31202             ['xs', 'xs', 'xs', 'tall'],
31203             ['xs', 'xs', 'tall'],
31204             ['xs', 'xs', 'sm'],
31205             ['xs', 'xs', 'xs'],
31206             ['xs', 'tall'],
31207             ['xs', 'sm'],
31208             ['xs', 'xs'],
31209             ['xs'],
31210             
31211             ['sm', 'xs', 'xs'],
31212             ['sm', 'xs'],
31213             ['sm'],
31214             
31215             ['tall', 'xs', 'xs', 'xs'],
31216             ['tall', 'xs', 'xs'],
31217             ['tall', 'xs'],
31218             ['tall']
31219             
31220         ];
31221         
31222         var queue = [];
31223         
31224         var boxes = [];
31225         
31226         var box = [];
31227         
31228         Roo.each(items, function(item, k){
31229             
31230             switch (item.size) {
31231                 // these layouts take up a full box,
31232                 case 'md' :
31233                 case 'md-left' :
31234                 case 'md-right' :
31235                 case 'wide' :
31236                     
31237                     if(box.length){
31238                         boxes.push(box);
31239                         box = [];
31240                     }
31241                     
31242                     boxes.push([item]);
31243                     
31244                     break;
31245                     
31246                 case 'xs' :
31247                 case 'sm' :
31248                 case 'tall' :
31249                     
31250                     box.push(item);
31251                     
31252                     break;
31253                 default :
31254                     break;
31255                     
31256             }
31257             
31258         }, this);
31259         
31260         if(box.length){
31261             boxes.push(box);
31262             box = [];
31263         }
31264         
31265         var filterPattern = function(box, length)
31266         {
31267             if(!box.length){
31268                 return;
31269             }
31270             
31271             var match = false;
31272             
31273             var pattern = box.slice(0, length);
31274             
31275             var format = [];
31276             
31277             Roo.each(pattern, function(i){
31278                 format.push(i.size);
31279             }, this);
31280             
31281             Roo.each(standard, function(s){
31282                 
31283                 if(String(s) != String(format)){
31284                     return;
31285                 }
31286                 
31287                 match = true;
31288                 return false;
31289                 
31290             }, this);
31291             
31292             if(!match && length == 1){
31293                 return;
31294             }
31295             
31296             if(!match){
31297                 filterPattern(box, length - 1);
31298                 return;
31299             }
31300                 
31301             queue.push(pattern);
31302
31303             box = box.slice(length, box.length);
31304
31305             filterPattern(box, 4);
31306
31307             return;
31308             
31309         }
31310         
31311         Roo.each(boxes, function(box, k){
31312             
31313             if(!box.length){
31314                 return;
31315             }
31316             
31317             if(box.length == 1){
31318                 queue.push(box);
31319                 return;
31320             }
31321             
31322             filterPattern(box, 4);
31323             
31324         }, this);
31325         
31326         this._processVerticalLayoutQueue( queue, isInstant );
31327         
31328     },
31329     
31330 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31331 //    {
31332 //        if ( !items || !items.length ) {
31333 //            return;
31334 //        }
31335 //
31336 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31337 //        
31338 //    },
31339     
31340     _horizontalLayoutItems : function ( items , isInstant)
31341     {
31342         if ( !items || !items.length || items.length < 3) {
31343             return;
31344         }
31345         
31346         items.reverse();
31347         
31348         var eItems = items.slice(0, 3);
31349         
31350         items = items.slice(3, items.length);
31351         
31352         var standard = [
31353             ['xs', 'xs', 'xs', 'wide'],
31354             ['xs', 'xs', 'wide'],
31355             ['xs', 'xs', 'sm'],
31356             ['xs', 'xs', 'xs'],
31357             ['xs', 'wide'],
31358             ['xs', 'sm'],
31359             ['xs', 'xs'],
31360             ['xs'],
31361             
31362             ['sm', 'xs', 'xs'],
31363             ['sm', 'xs'],
31364             ['sm'],
31365             
31366             ['wide', 'xs', 'xs', 'xs'],
31367             ['wide', 'xs', 'xs'],
31368             ['wide', 'xs'],
31369             ['wide'],
31370             
31371             ['wide-thin']
31372         ];
31373         
31374         var queue = [];
31375         
31376         var boxes = [];
31377         
31378         var box = [];
31379         
31380         Roo.each(items, function(item, k){
31381             
31382             switch (item.size) {
31383                 case 'md' :
31384                 case 'md-left' :
31385                 case 'md-right' :
31386                 case 'tall' :
31387                     
31388                     if(box.length){
31389                         boxes.push(box);
31390                         box = [];
31391                     }
31392                     
31393                     boxes.push([item]);
31394                     
31395                     break;
31396                     
31397                 case 'xs' :
31398                 case 'sm' :
31399                 case 'wide' :
31400                 case 'wide-thin' :
31401                     
31402                     box.push(item);
31403                     
31404                     break;
31405                 default :
31406                     break;
31407                     
31408             }
31409             
31410         }, this);
31411         
31412         if(box.length){
31413             boxes.push(box);
31414             box = [];
31415         }
31416         
31417         var filterPattern = function(box, length)
31418         {
31419             if(!box.length){
31420                 return;
31421             }
31422             
31423             var match = false;
31424             
31425             var pattern = box.slice(0, length);
31426             
31427             var format = [];
31428             
31429             Roo.each(pattern, function(i){
31430                 format.push(i.size);
31431             }, this);
31432             
31433             Roo.each(standard, function(s){
31434                 
31435                 if(String(s) != String(format)){
31436                     return;
31437                 }
31438                 
31439                 match = true;
31440                 return false;
31441                 
31442             }, this);
31443             
31444             if(!match && length == 1){
31445                 return;
31446             }
31447             
31448             if(!match){
31449                 filterPattern(box, length - 1);
31450                 return;
31451             }
31452                 
31453             queue.push(pattern);
31454
31455             box = box.slice(length, box.length);
31456
31457             filterPattern(box, 4);
31458
31459             return;
31460             
31461         }
31462         
31463         Roo.each(boxes, function(box, k){
31464             
31465             if(!box.length){
31466                 return;
31467             }
31468             
31469             if(box.length == 1){
31470                 queue.push(box);
31471                 return;
31472             }
31473             
31474             filterPattern(box, 4);
31475             
31476         }, this);
31477         
31478         
31479         var prune = [];
31480         
31481         var pos = this.el.getBox(true);
31482         
31483         var minX = pos.x;
31484         
31485         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31486         
31487         var hit_end = false;
31488         
31489         Roo.each(queue, function(box){
31490             
31491             if(hit_end){
31492                 
31493                 Roo.each(box, function(b){
31494                 
31495                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31496                     b.el.hide();
31497
31498                 }, this);
31499
31500                 return;
31501             }
31502             
31503             var mx = 0;
31504             
31505             Roo.each(box, function(b){
31506                 
31507                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31508                 b.el.show();
31509
31510                 mx = Math.max(mx, b.x);
31511                 
31512             }, this);
31513             
31514             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31515             
31516             if(maxX < minX){
31517                 
31518                 Roo.each(box, function(b){
31519                 
31520                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31521                     b.el.hide();
31522                     
31523                 }, this);
31524                 
31525                 hit_end = true;
31526                 
31527                 return;
31528             }
31529             
31530             prune.push(box);
31531             
31532         }, this);
31533         
31534         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31535     },
31536     
31537     /** Sets position of item in DOM
31538     * @param {Element} item
31539     * @param {Number} x - horizontal position
31540     * @param {Number} y - vertical position
31541     * @param {Boolean} isInstant - disables transitions
31542     */
31543     _processVerticalLayoutQueue : function( queue, isInstant )
31544     {
31545         var pos = this.el.getBox(true);
31546         var x = pos.x;
31547         var y = pos.y;
31548         var maxY = [];
31549         
31550         for (var i = 0; i < this.cols; i++){
31551             maxY[i] = pos.y;
31552         }
31553         
31554         Roo.each(queue, function(box, k){
31555             
31556             var col = k % this.cols;
31557             
31558             Roo.each(box, function(b,kk){
31559                 
31560                 b.el.position('absolute');
31561                 
31562                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31563                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31564                 
31565                 if(b.size == 'md-left' || b.size == 'md-right'){
31566                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31567                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31568                 }
31569                 
31570                 b.el.setWidth(width);
31571                 b.el.setHeight(height);
31572                 // iframe?
31573                 b.el.select('iframe',true).setSize(width,height);
31574                 
31575             }, this);
31576             
31577             for (var i = 0; i < this.cols; i++){
31578                 
31579                 if(maxY[i] < maxY[col]){
31580                     col = i;
31581                     continue;
31582                 }
31583                 
31584                 col = Math.min(col, i);
31585                 
31586             }
31587             
31588             x = pos.x + col * (this.colWidth + this.padWidth);
31589             
31590             y = maxY[col];
31591             
31592             var positions = [];
31593             
31594             switch (box.length){
31595                 case 1 :
31596                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31597                     break;
31598                 case 2 :
31599                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31600                     break;
31601                 case 3 :
31602                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31603                     break;
31604                 case 4 :
31605                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31606                     break;
31607                 default :
31608                     break;
31609             }
31610             
31611             Roo.each(box, function(b,kk){
31612                 
31613                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31614                 
31615                 var sz = b.el.getSize();
31616                 
31617                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31618                 
31619             }, this);
31620             
31621         }, this);
31622         
31623         var mY = 0;
31624         
31625         for (var i = 0; i < this.cols; i++){
31626             mY = Math.max(mY, maxY[i]);
31627         }
31628         
31629         this.el.setHeight(mY - pos.y);
31630         
31631     },
31632     
31633 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31634 //    {
31635 //        var pos = this.el.getBox(true);
31636 //        var x = pos.x;
31637 //        var y = pos.y;
31638 //        var maxX = pos.right;
31639 //        
31640 //        var maxHeight = 0;
31641 //        
31642 //        Roo.each(items, function(item, k){
31643 //            
31644 //            var c = k % 2;
31645 //            
31646 //            item.el.position('absolute');
31647 //                
31648 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31649 //
31650 //            item.el.setWidth(width);
31651 //
31652 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31653 //
31654 //            item.el.setHeight(height);
31655 //            
31656 //            if(c == 0){
31657 //                item.el.setXY([x, y], isInstant ? false : true);
31658 //            } else {
31659 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31660 //            }
31661 //            
31662 //            y = y + height + this.alternativePadWidth;
31663 //            
31664 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31665 //            
31666 //        }, this);
31667 //        
31668 //        this.el.setHeight(maxHeight);
31669 //        
31670 //    },
31671     
31672     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31673     {
31674         var pos = this.el.getBox(true);
31675         
31676         var minX = pos.x;
31677         var minY = pos.y;
31678         
31679         var maxX = pos.right;
31680         
31681         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31682         
31683         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31684         
31685         Roo.each(queue, function(box, k){
31686             
31687             Roo.each(box, function(b, kk){
31688                 
31689                 b.el.position('absolute');
31690                 
31691                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31692                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31693                 
31694                 if(b.size == 'md-left' || b.size == 'md-right'){
31695                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31696                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31697                 }
31698                 
31699                 b.el.setWidth(width);
31700                 b.el.setHeight(height);
31701                 
31702             }, this);
31703             
31704             if(!box.length){
31705                 return;
31706             }
31707             
31708             var positions = [];
31709             
31710             switch (box.length){
31711                 case 1 :
31712                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31713                     break;
31714                 case 2 :
31715                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31716                     break;
31717                 case 3 :
31718                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31719                     break;
31720                 case 4 :
31721                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31722                     break;
31723                 default :
31724                     break;
31725             }
31726             
31727             Roo.each(box, function(b,kk){
31728                 
31729                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31730                 
31731                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31732                 
31733             }, this);
31734             
31735         }, this);
31736         
31737     },
31738     
31739     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31740     {
31741         Roo.each(eItems, function(b,k){
31742             
31743             b.size = (k == 0) ? 'sm' : 'xs';
31744             b.x = (k == 0) ? 2 : 1;
31745             b.y = (k == 0) ? 2 : 1;
31746             
31747             b.el.position('absolute');
31748             
31749             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31750                 
31751             b.el.setWidth(width);
31752             
31753             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31754             
31755             b.el.setHeight(height);
31756             
31757         }, this);
31758
31759         var positions = [];
31760         
31761         positions.push({
31762             x : maxX - this.unitWidth * 2 - this.gutter,
31763             y : minY
31764         });
31765         
31766         positions.push({
31767             x : maxX - this.unitWidth,
31768             y : minY + (this.unitWidth + this.gutter) * 2
31769         });
31770         
31771         positions.push({
31772             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31773             y : minY
31774         });
31775         
31776         Roo.each(eItems, function(b,k){
31777             
31778             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31779
31780         }, this);
31781         
31782     },
31783     
31784     getVerticalOneBoxColPositions : function(x, y, box)
31785     {
31786         var pos = [];
31787         
31788         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31789         
31790         if(box[0].size == 'md-left'){
31791             rand = 0;
31792         }
31793         
31794         if(box[0].size == 'md-right'){
31795             rand = 1;
31796         }
31797         
31798         pos.push({
31799             x : x + (this.unitWidth + this.gutter) * rand,
31800             y : y
31801         });
31802         
31803         return pos;
31804     },
31805     
31806     getVerticalTwoBoxColPositions : function(x, y, box)
31807     {
31808         var pos = [];
31809         
31810         if(box[0].size == 'xs'){
31811             
31812             pos.push({
31813                 x : x,
31814                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31815             });
31816
31817             pos.push({
31818                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31819                 y : y
31820             });
31821             
31822             return pos;
31823             
31824         }
31825         
31826         pos.push({
31827             x : x,
31828             y : y
31829         });
31830
31831         pos.push({
31832             x : x + (this.unitWidth + this.gutter) * 2,
31833             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31834         });
31835         
31836         return pos;
31837         
31838     },
31839     
31840     getVerticalThreeBoxColPositions : function(x, y, box)
31841     {
31842         var pos = [];
31843         
31844         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31845             
31846             pos.push({
31847                 x : x,
31848                 y : y
31849             });
31850
31851             pos.push({
31852                 x : x + (this.unitWidth + this.gutter) * 1,
31853                 y : y
31854             });
31855             
31856             pos.push({
31857                 x : x + (this.unitWidth + this.gutter) * 2,
31858                 y : y
31859             });
31860             
31861             return pos;
31862             
31863         }
31864         
31865         if(box[0].size == 'xs' && box[1].size == 'xs'){
31866             
31867             pos.push({
31868                 x : x,
31869                 y : y
31870             });
31871
31872             pos.push({
31873                 x : x,
31874                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31875             });
31876             
31877             pos.push({
31878                 x : x + (this.unitWidth + this.gutter) * 1,
31879                 y : y
31880             });
31881             
31882             return pos;
31883             
31884         }
31885         
31886         pos.push({
31887             x : x,
31888             y : y
31889         });
31890
31891         pos.push({
31892             x : x + (this.unitWidth + this.gutter) * 2,
31893             y : y
31894         });
31895
31896         pos.push({
31897             x : x + (this.unitWidth + this.gutter) * 2,
31898             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31899         });
31900             
31901         return pos;
31902         
31903     },
31904     
31905     getVerticalFourBoxColPositions : function(x, y, box)
31906     {
31907         var pos = [];
31908         
31909         if(box[0].size == 'xs'){
31910             
31911             pos.push({
31912                 x : x,
31913                 y : y
31914             });
31915
31916             pos.push({
31917                 x : x,
31918                 y : y + (this.unitHeight + this.gutter) * 1
31919             });
31920             
31921             pos.push({
31922                 x : x,
31923                 y : y + (this.unitHeight + this.gutter) * 2
31924             });
31925             
31926             pos.push({
31927                 x : x + (this.unitWidth + this.gutter) * 1,
31928                 y : y
31929             });
31930             
31931             return pos;
31932             
31933         }
31934         
31935         pos.push({
31936             x : x,
31937             y : y
31938         });
31939
31940         pos.push({
31941             x : x + (this.unitWidth + this.gutter) * 2,
31942             y : y
31943         });
31944
31945         pos.push({
31946             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31947             y : y + (this.unitHeight + this.gutter) * 1
31948         });
31949
31950         pos.push({
31951             x : x + (this.unitWidth + this.gutter) * 2,
31952             y : y + (this.unitWidth + this.gutter) * 2
31953         });
31954
31955         return pos;
31956         
31957     },
31958     
31959     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31960     {
31961         var pos = [];
31962         
31963         if(box[0].size == 'md-left'){
31964             pos.push({
31965                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31966                 y : minY
31967             });
31968             
31969             return pos;
31970         }
31971         
31972         if(box[0].size == 'md-right'){
31973             pos.push({
31974                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31975                 y : minY + (this.unitWidth + this.gutter) * 1
31976             });
31977             
31978             return pos;
31979         }
31980         
31981         var rand = Math.floor(Math.random() * (4 - box[0].y));
31982         
31983         pos.push({
31984             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31985             y : minY + (this.unitWidth + this.gutter) * rand
31986         });
31987         
31988         return pos;
31989         
31990     },
31991     
31992     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31993     {
31994         var pos = [];
31995         
31996         if(box[0].size == 'xs'){
31997             
31998             pos.push({
31999                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32000                 y : minY
32001             });
32002
32003             pos.push({
32004                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32005                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32006             });
32007             
32008             return pos;
32009             
32010         }
32011         
32012         pos.push({
32013             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32014             y : minY
32015         });
32016
32017         pos.push({
32018             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32019             y : minY + (this.unitWidth + this.gutter) * 2
32020         });
32021         
32022         return pos;
32023         
32024     },
32025     
32026     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32027     {
32028         var pos = [];
32029         
32030         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32031             
32032             pos.push({
32033                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32034                 y : minY
32035             });
32036
32037             pos.push({
32038                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32039                 y : minY + (this.unitWidth + this.gutter) * 1
32040             });
32041             
32042             pos.push({
32043                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32044                 y : minY + (this.unitWidth + this.gutter) * 2
32045             });
32046             
32047             return pos;
32048             
32049         }
32050         
32051         if(box[0].size == 'xs' && box[1].size == 'xs'){
32052             
32053             pos.push({
32054                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32055                 y : minY
32056             });
32057
32058             pos.push({
32059                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32060                 y : minY
32061             });
32062             
32063             pos.push({
32064                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32065                 y : minY + (this.unitWidth + this.gutter) * 1
32066             });
32067             
32068             return pos;
32069             
32070         }
32071         
32072         pos.push({
32073             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32074             y : minY
32075         });
32076
32077         pos.push({
32078             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32079             y : minY + (this.unitWidth + this.gutter) * 2
32080         });
32081
32082         pos.push({
32083             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32084             y : minY + (this.unitWidth + this.gutter) * 2
32085         });
32086             
32087         return pos;
32088         
32089     },
32090     
32091     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32092     {
32093         var pos = [];
32094         
32095         if(box[0].size == 'xs'){
32096             
32097             pos.push({
32098                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32099                 y : minY
32100             });
32101
32102             pos.push({
32103                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32104                 y : minY
32105             });
32106             
32107             pos.push({
32108                 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),
32109                 y : minY
32110             });
32111             
32112             pos.push({
32113                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32114                 y : minY + (this.unitWidth + this.gutter) * 1
32115             });
32116             
32117             return pos;
32118             
32119         }
32120         
32121         pos.push({
32122             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32123             y : minY
32124         });
32125         
32126         pos.push({
32127             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32128             y : minY + (this.unitWidth + this.gutter) * 2
32129         });
32130         
32131         pos.push({
32132             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32133             y : minY + (this.unitWidth + this.gutter) * 2
32134         });
32135         
32136         pos.push({
32137             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),
32138             y : minY + (this.unitWidth + this.gutter) * 2
32139         });
32140
32141         return pos;
32142         
32143     },
32144     
32145     /**
32146     * remove a Masonry Brick
32147     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32148     */
32149     removeBrick : function(brick_id)
32150     {
32151         if (!brick_id) {
32152             return;
32153         }
32154         
32155         for (var i = 0; i<this.bricks.length; i++) {
32156             if (this.bricks[i].id == brick_id) {
32157                 this.bricks.splice(i,1);
32158                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32159                 this.initial();
32160             }
32161         }
32162     },
32163     
32164     /**
32165     * adds a Masonry Brick
32166     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32167     */
32168     addBrick : function(cfg)
32169     {
32170         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32171         //this.register(cn);
32172         cn.parentId = this.id;
32173         cn.render(this.el);
32174         return cn;
32175     },
32176     
32177     /**
32178     * register a Masonry Brick
32179     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32180     */
32181     
32182     register : function(brick)
32183     {
32184         this.bricks.push(brick);
32185         brick.masonryId = this.id;
32186     },
32187     
32188     /**
32189     * clear all the Masonry Brick
32190     */
32191     clearAll : function()
32192     {
32193         this.bricks = [];
32194         //this.getChildContainer().dom.innerHTML = "";
32195         this.el.dom.innerHTML = '';
32196     },
32197     
32198     getSelected : function()
32199     {
32200         if (!this.selectedBrick) {
32201             return false;
32202         }
32203         
32204         return this.selectedBrick;
32205     }
32206 });
32207
32208 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32209     
32210     groups: {},
32211      /**
32212     * register a Masonry Layout
32213     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32214     */
32215     
32216     register : function(layout)
32217     {
32218         this.groups[layout.id] = layout;
32219     },
32220     /**
32221     * fetch a  Masonry Layout based on the masonry layout ID
32222     * @param {string} the masonry layout to add
32223     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32224     */
32225     
32226     get: function(layout_id) {
32227         if (typeof(this.groups[layout_id]) == 'undefined') {
32228             return false;
32229         }
32230         return this.groups[layout_id] ;
32231     }
32232     
32233     
32234     
32235 });
32236
32237  
32238
32239  /**
32240  *
32241  * This is based on 
32242  * http://masonry.desandro.com
32243  *
32244  * The idea is to render all the bricks based on vertical width...
32245  *
32246  * The original code extends 'outlayer' - we might need to use that....
32247  * 
32248  */
32249
32250
32251 /**
32252  * @class Roo.bootstrap.LayoutMasonryAuto
32253  * @extends Roo.bootstrap.Component
32254  * Bootstrap Layout Masonry class
32255  * 
32256  * @constructor
32257  * Create a new Element
32258  * @param {Object} config The config object
32259  */
32260
32261 Roo.bootstrap.LayoutMasonryAuto = function(config){
32262     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32263 };
32264
32265 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32266     
32267       /**
32268      * @cfg {Boolean} isFitWidth  - resize the width..
32269      */   
32270     isFitWidth : false,  // options..
32271     /**
32272      * @cfg {Boolean} isOriginLeft = left align?
32273      */   
32274     isOriginLeft : true,
32275     /**
32276      * @cfg {Boolean} isOriginTop = top align?
32277      */   
32278     isOriginTop : false,
32279     /**
32280      * @cfg {Boolean} isLayoutInstant = no animation?
32281      */   
32282     isLayoutInstant : false, // needed?
32283     /**
32284      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32285      */   
32286     isResizingContainer : true,
32287     /**
32288      * @cfg {Number} columnWidth  width of the columns 
32289      */   
32290     
32291     columnWidth : 0,
32292     
32293     /**
32294      * @cfg {Number} maxCols maximum number of columns
32295      */   
32296     
32297     maxCols: 0,
32298     /**
32299      * @cfg {Number} padHeight padding below box..
32300      */   
32301     
32302     padHeight : 10, 
32303     
32304     /**
32305      * @cfg {Boolean} isAutoInitial defalut true
32306      */   
32307     
32308     isAutoInitial : true, 
32309     
32310     // private?
32311     gutter : 0,
32312     
32313     containerWidth: 0,
32314     initialColumnWidth : 0,
32315     currentSize : null,
32316     
32317     colYs : null, // array.
32318     maxY : 0,
32319     padWidth: 10,
32320     
32321     
32322     tag: 'div',
32323     cls: '',
32324     bricks: null, //CompositeElement
32325     cols : 0, // array?
32326     // element : null, // wrapped now this.el
32327     _isLayoutInited : null, 
32328     
32329     
32330     getAutoCreate : function(){
32331         
32332         var cfg = {
32333             tag: this.tag,
32334             cls: 'blog-masonary-wrapper ' + this.cls,
32335             cn : {
32336                 cls : 'mas-boxes masonary'
32337             }
32338         };
32339         
32340         return cfg;
32341     },
32342     
32343     getChildContainer: function( )
32344     {
32345         if (this.boxesEl) {
32346             return this.boxesEl;
32347         }
32348         
32349         this.boxesEl = this.el.select('.mas-boxes').first();
32350         
32351         return this.boxesEl;
32352     },
32353     
32354     
32355     initEvents : function()
32356     {
32357         var _this = this;
32358         
32359         if(this.isAutoInitial){
32360             Roo.log('hook children rendered');
32361             this.on('childrenrendered', function() {
32362                 Roo.log('children rendered');
32363                 _this.initial();
32364             } ,this);
32365         }
32366         
32367     },
32368     
32369     initial : function()
32370     {
32371         this.reloadItems();
32372
32373         this.currentSize = this.el.getBox(true);
32374
32375         /// was window resize... - let's see if this works..
32376         Roo.EventManager.onWindowResize(this.resize, this); 
32377
32378         if(!this.isAutoInitial){
32379             this.layout();
32380             return;
32381         }
32382         
32383         this.layout.defer(500,this);
32384     },
32385     
32386     reloadItems: function()
32387     {
32388         this.bricks = this.el.select('.masonry-brick', true);
32389         
32390         this.bricks.each(function(b) {
32391             //Roo.log(b.getSize());
32392             if (!b.attr('originalwidth')) {
32393                 b.attr('originalwidth',  b.getSize().width);
32394             }
32395             
32396         });
32397         
32398         Roo.log(this.bricks.elements.length);
32399     },
32400     
32401     resize : function()
32402     {
32403         Roo.log('resize');
32404         var cs = this.el.getBox(true);
32405         
32406         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32407             Roo.log("no change in with or X");
32408             return;
32409         }
32410         this.currentSize = cs;
32411         this.layout();
32412     },
32413     
32414     layout : function()
32415     {
32416          Roo.log('layout');
32417         this._resetLayout();
32418         //this._manageStamps();
32419       
32420         // don't animate first layout
32421         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32422         this.layoutItems( isInstant );
32423       
32424         // flag for initalized
32425         this._isLayoutInited = true;
32426     },
32427     
32428     layoutItems : function( isInstant )
32429     {
32430         //var items = this._getItemsForLayout( this.items );
32431         // original code supports filtering layout items.. we just ignore it..
32432         
32433         this._layoutItems( this.bricks , isInstant );
32434       
32435         this._postLayout();
32436     },
32437     _layoutItems : function ( items , isInstant)
32438     {
32439        //this.fireEvent( 'layout', this, items );
32440     
32441
32442         if ( !items || !items.elements.length ) {
32443           // no items, emit event with empty array
32444             return;
32445         }
32446
32447         var queue = [];
32448         items.each(function(item) {
32449             Roo.log("layout item");
32450             Roo.log(item);
32451             // get x/y object from method
32452             var position = this._getItemLayoutPosition( item );
32453             // enqueue
32454             position.item = item;
32455             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32456             queue.push( position );
32457         }, this);
32458       
32459         this._processLayoutQueue( queue );
32460     },
32461     /** Sets position of item in DOM
32462     * @param {Element} item
32463     * @param {Number} x - horizontal position
32464     * @param {Number} y - vertical position
32465     * @param {Boolean} isInstant - disables transitions
32466     */
32467     _processLayoutQueue : function( queue )
32468     {
32469         for ( var i=0, len = queue.length; i < len; i++ ) {
32470             var obj = queue[i];
32471             obj.item.position('absolute');
32472             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32473         }
32474     },
32475       
32476     
32477     /**
32478     * Any logic you want to do after each layout,
32479     * i.e. size the container
32480     */
32481     _postLayout : function()
32482     {
32483         this.resizeContainer();
32484     },
32485     
32486     resizeContainer : function()
32487     {
32488         if ( !this.isResizingContainer ) {
32489             return;
32490         }
32491         var size = this._getContainerSize();
32492         if ( size ) {
32493             this.el.setSize(size.width,size.height);
32494             this.boxesEl.setSize(size.width,size.height);
32495         }
32496     },
32497     
32498     
32499     
32500     _resetLayout : function()
32501     {
32502         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32503         this.colWidth = this.el.getWidth();
32504         //this.gutter = this.el.getWidth(); 
32505         
32506         this.measureColumns();
32507
32508         // reset column Y
32509         var i = this.cols;
32510         this.colYs = [];
32511         while (i--) {
32512             this.colYs.push( 0 );
32513         }
32514     
32515         this.maxY = 0;
32516     },
32517
32518     measureColumns : function()
32519     {
32520         this.getContainerWidth();
32521       // if columnWidth is 0, default to outerWidth of first item
32522         if ( !this.columnWidth ) {
32523             var firstItem = this.bricks.first();
32524             Roo.log(firstItem);
32525             this.columnWidth  = this.containerWidth;
32526             if (firstItem && firstItem.attr('originalwidth') ) {
32527                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32528             }
32529             // columnWidth fall back to item of first element
32530             Roo.log("set column width?");
32531                         this.initialColumnWidth = this.columnWidth  ;
32532
32533             // if first elem has no width, default to size of container
32534             
32535         }
32536         
32537         
32538         if (this.initialColumnWidth) {
32539             this.columnWidth = this.initialColumnWidth;
32540         }
32541         
32542         
32543             
32544         // column width is fixed at the top - however if container width get's smaller we should
32545         // reduce it...
32546         
32547         // this bit calcs how man columns..
32548             
32549         var columnWidth = this.columnWidth += this.gutter;
32550       
32551         // calculate columns
32552         var containerWidth = this.containerWidth + this.gutter;
32553         
32554         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32555         // fix rounding errors, typically with gutters
32556         var excess = columnWidth - containerWidth % columnWidth;
32557         
32558         
32559         // if overshoot is less than a pixel, round up, otherwise floor it
32560         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32561         cols = Math[ mathMethod ]( cols );
32562         this.cols = Math.max( cols, 1 );
32563         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32564         
32565          // padding positioning..
32566         var totalColWidth = this.cols * this.columnWidth;
32567         var padavail = this.containerWidth - totalColWidth;
32568         // so for 2 columns - we need 3 'pads'
32569         
32570         var padNeeded = (1+this.cols) * this.padWidth;
32571         
32572         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32573         
32574         this.columnWidth += padExtra
32575         //this.padWidth = Math.floor(padavail /  ( this.cols));
32576         
32577         // adjust colum width so that padding is fixed??
32578         
32579         // we have 3 columns ... total = width * 3
32580         // we have X left over... that should be used by 
32581         
32582         //if (this.expandC) {
32583             
32584         //}
32585         
32586         
32587         
32588     },
32589     
32590     getContainerWidth : function()
32591     {
32592        /* // container is parent if fit width
32593         var container = this.isFitWidth ? this.element.parentNode : this.element;
32594         // check that this.size and size are there
32595         // IE8 triggers resize on body size change, so they might not be
32596         
32597         var size = getSize( container );  //FIXME
32598         this.containerWidth = size && size.innerWidth; //FIXME
32599         */
32600          
32601         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32602         
32603     },
32604     
32605     _getItemLayoutPosition : function( item )  // what is item?
32606     {
32607         // we resize the item to our columnWidth..
32608       
32609         item.setWidth(this.columnWidth);
32610         item.autoBoxAdjust  = false;
32611         
32612         var sz = item.getSize();
32613  
32614         // how many columns does this brick span
32615         var remainder = this.containerWidth % this.columnWidth;
32616         
32617         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32618         // round if off by 1 pixel, otherwise use ceil
32619         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32620         colSpan = Math.min( colSpan, this.cols );
32621         
32622         // normally this should be '1' as we dont' currently allow multi width columns..
32623         
32624         var colGroup = this._getColGroup( colSpan );
32625         // get the minimum Y value from the columns
32626         var minimumY = Math.min.apply( Math, colGroup );
32627         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32628         
32629         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32630          
32631         // position the brick
32632         var position = {
32633             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32634             y: this.currentSize.y + minimumY + this.padHeight
32635         };
32636         
32637         Roo.log(position);
32638         // apply setHeight to necessary columns
32639         var setHeight = minimumY + sz.height + this.padHeight;
32640         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32641         
32642         var setSpan = this.cols + 1 - colGroup.length;
32643         for ( var i = 0; i < setSpan; i++ ) {
32644           this.colYs[ shortColIndex + i ] = setHeight ;
32645         }
32646       
32647         return position;
32648     },
32649     
32650     /**
32651      * @param {Number} colSpan - number of columns the element spans
32652      * @returns {Array} colGroup
32653      */
32654     _getColGroup : function( colSpan )
32655     {
32656         if ( colSpan < 2 ) {
32657           // if brick spans only one column, use all the column Ys
32658           return this.colYs;
32659         }
32660       
32661         var colGroup = [];
32662         // how many different places could this brick fit horizontally
32663         var groupCount = this.cols + 1 - colSpan;
32664         // for each group potential horizontal position
32665         for ( var i = 0; i < groupCount; i++ ) {
32666           // make an array of colY values for that one group
32667           var groupColYs = this.colYs.slice( i, i + colSpan );
32668           // and get the max value of the array
32669           colGroup[i] = Math.max.apply( Math, groupColYs );
32670         }
32671         return colGroup;
32672     },
32673     /*
32674     _manageStamp : function( stamp )
32675     {
32676         var stampSize =  stamp.getSize();
32677         var offset = stamp.getBox();
32678         // get the columns that this stamp affects
32679         var firstX = this.isOriginLeft ? offset.x : offset.right;
32680         var lastX = firstX + stampSize.width;
32681         var firstCol = Math.floor( firstX / this.columnWidth );
32682         firstCol = Math.max( 0, firstCol );
32683         
32684         var lastCol = Math.floor( lastX / this.columnWidth );
32685         // lastCol should not go over if multiple of columnWidth #425
32686         lastCol -= lastX % this.columnWidth ? 0 : 1;
32687         lastCol = Math.min( this.cols - 1, lastCol );
32688         
32689         // set colYs to bottom of the stamp
32690         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32691             stampSize.height;
32692             
32693         for ( var i = firstCol; i <= lastCol; i++ ) {
32694           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32695         }
32696     },
32697     */
32698     
32699     _getContainerSize : function()
32700     {
32701         this.maxY = Math.max.apply( Math, this.colYs );
32702         var size = {
32703             height: this.maxY
32704         };
32705       
32706         if ( this.isFitWidth ) {
32707             size.width = this._getContainerFitWidth();
32708         }
32709       
32710         return size;
32711     },
32712     
32713     _getContainerFitWidth : function()
32714     {
32715         var unusedCols = 0;
32716         // count unused columns
32717         var i = this.cols;
32718         while ( --i ) {
32719           if ( this.colYs[i] !== 0 ) {
32720             break;
32721           }
32722           unusedCols++;
32723         }
32724         // fit container to columns that have been used
32725         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32726     },
32727     
32728     needsResizeLayout : function()
32729     {
32730         var previousWidth = this.containerWidth;
32731         this.getContainerWidth();
32732         return previousWidth !== this.containerWidth;
32733     }
32734  
32735 });
32736
32737  
32738
32739  /*
32740  * - LGPL
32741  *
32742  * element
32743  * 
32744  */
32745
32746 /**
32747  * @class Roo.bootstrap.MasonryBrick
32748  * @extends Roo.bootstrap.Component
32749  * Bootstrap MasonryBrick class
32750  * 
32751  * @constructor
32752  * Create a new MasonryBrick
32753  * @param {Object} config The config object
32754  */
32755
32756 Roo.bootstrap.MasonryBrick = function(config){
32757     
32758     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32759     
32760     Roo.bootstrap.MasonryBrick.register(this);
32761     
32762     this.addEvents({
32763         // raw events
32764         /**
32765          * @event click
32766          * When a MasonryBrick is clcik
32767          * @param {Roo.bootstrap.MasonryBrick} this
32768          * @param {Roo.EventObject} e
32769          */
32770         "click" : true
32771     });
32772 };
32773
32774 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32775     
32776     /**
32777      * @cfg {String} title
32778      */   
32779     title : '',
32780     /**
32781      * @cfg {String} html
32782      */   
32783     html : '',
32784     /**
32785      * @cfg {String} bgimage
32786      */   
32787     bgimage : '',
32788     /**
32789      * @cfg {String} videourl
32790      */   
32791     videourl : '',
32792     /**
32793      * @cfg {String} cls
32794      */   
32795     cls : '',
32796     /**
32797      * @cfg {String} href
32798      */   
32799     href : '',
32800     /**
32801      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32802      */   
32803     size : 'xs',
32804     
32805     /**
32806      * @cfg {String} placetitle (center|bottom)
32807      */   
32808     placetitle : '',
32809     
32810     /**
32811      * @cfg {Boolean} isFitContainer defalut true
32812      */   
32813     isFitContainer : true, 
32814     
32815     /**
32816      * @cfg {Boolean} preventDefault defalut false
32817      */   
32818     preventDefault : false, 
32819     
32820     /**
32821      * @cfg {Boolean} inverse defalut false
32822      */   
32823     maskInverse : false, 
32824     
32825     getAutoCreate : function()
32826     {
32827         if(!this.isFitContainer){
32828             return this.getSplitAutoCreate();
32829         }
32830         
32831         var cls = 'masonry-brick masonry-brick-full';
32832         
32833         if(this.href.length){
32834             cls += ' masonry-brick-link';
32835         }
32836         
32837         if(this.bgimage.length){
32838             cls += ' masonry-brick-image';
32839         }
32840         
32841         if(this.maskInverse){
32842             cls += ' mask-inverse';
32843         }
32844         
32845         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32846             cls += ' enable-mask';
32847         }
32848         
32849         if(this.size){
32850             cls += ' masonry-' + this.size + '-brick';
32851         }
32852         
32853         if(this.placetitle.length){
32854             
32855             switch (this.placetitle) {
32856                 case 'center' :
32857                     cls += ' masonry-center-title';
32858                     break;
32859                 case 'bottom' :
32860                     cls += ' masonry-bottom-title';
32861                     break;
32862                 default:
32863                     break;
32864             }
32865             
32866         } else {
32867             if(!this.html.length && !this.bgimage.length){
32868                 cls += ' masonry-center-title';
32869             }
32870
32871             if(!this.html.length && this.bgimage.length){
32872                 cls += ' masonry-bottom-title';
32873             }
32874         }
32875         
32876         if(this.cls){
32877             cls += ' ' + this.cls;
32878         }
32879         
32880         var cfg = {
32881             tag: (this.href.length) ? 'a' : 'div',
32882             cls: cls,
32883             cn: [
32884                 {
32885                     tag: 'div',
32886                     cls: 'masonry-brick-mask'
32887                 },
32888                 {
32889                     tag: 'div',
32890                     cls: 'masonry-brick-paragraph',
32891                     cn: []
32892                 }
32893             ]
32894         };
32895         
32896         if(this.href.length){
32897             cfg.href = this.href;
32898         }
32899         
32900         var cn = cfg.cn[1].cn;
32901         
32902         if(this.title.length){
32903             cn.push({
32904                 tag: 'h4',
32905                 cls: 'masonry-brick-title',
32906                 html: this.title
32907             });
32908         }
32909         
32910         if(this.html.length){
32911             cn.push({
32912                 tag: 'p',
32913                 cls: 'masonry-brick-text',
32914                 html: this.html
32915             });
32916         }
32917         
32918         if (!this.title.length && !this.html.length) {
32919             cfg.cn[1].cls += ' hide';
32920         }
32921         
32922         if(this.bgimage.length){
32923             cfg.cn.push({
32924                 tag: 'img',
32925                 cls: 'masonry-brick-image-view',
32926                 src: this.bgimage
32927             });
32928         }
32929         
32930         if(this.videourl.length){
32931             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32932             // youtube support only?
32933             cfg.cn.push({
32934                 tag: 'iframe',
32935                 cls: 'masonry-brick-image-view',
32936                 src: vurl,
32937                 frameborder : 0,
32938                 allowfullscreen : true
32939             });
32940         }
32941         
32942         return cfg;
32943         
32944     },
32945     
32946     getSplitAutoCreate : function()
32947     {
32948         var cls = 'masonry-brick masonry-brick-split';
32949         
32950         if(this.href.length){
32951             cls += ' masonry-brick-link';
32952         }
32953         
32954         if(this.bgimage.length){
32955             cls += ' masonry-brick-image';
32956         }
32957         
32958         if(this.size){
32959             cls += ' masonry-' + this.size + '-brick';
32960         }
32961         
32962         switch (this.placetitle) {
32963             case 'center' :
32964                 cls += ' masonry-center-title';
32965                 break;
32966             case 'bottom' :
32967                 cls += ' masonry-bottom-title';
32968                 break;
32969             default:
32970                 if(!this.bgimage.length){
32971                     cls += ' masonry-center-title';
32972                 }
32973
32974                 if(this.bgimage.length){
32975                     cls += ' masonry-bottom-title';
32976                 }
32977                 break;
32978         }
32979         
32980         if(this.cls){
32981             cls += ' ' + this.cls;
32982         }
32983         
32984         var cfg = {
32985             tag: (this.href.length) ? 'a' : 'div',
32986             cls: cls,
32987             cn: [
32988                 {
32989                     tag: 'div',
32990                     cls: 'masonry-brick-split-head',
32991                     cn: [
32992                         {
32993                             tag: 'div',
32994                             cls: 'masonry-brick-paragraph',
32995                             cn: []
32996                         }
32997                     ]
32998                 },
32999                 {
33000                     tag: 'div',
33001                     cls: 'masonry-brick-split-body',
33002                     cn: []
33003                 }
33004             ]
33005         };
33006         
33007         if(this.href.length){
33008             cfg.href = this.href;
33009         }
33010         
33011         if(this.title.length){
33012             cfg.cn[0].cn[0].cn.push({
33013                 tag: 'h4',
33014                 cls: 'masonry-brick-title',
33015                 html: this.title
33016             });
33017         }
33018         
33019         if(this.html.length){
33020             cfg.cn[1].cn.push({
33021                 tag: 'p',
33022                 cls: 'masonry-brick-text',
33023                 html: this.html
33024             });
33025         }
33026
33027         if(this.bgimage.length){
33028             cfg.cn[0].cn.push({
33029                 tag: 'img',
33030                 cls: 'masonry-brick-image-view',
33031                 src: this.bgimage
33032             });
33033         }
33034         
33035         if(this.videourl.length){
33036             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33037             // youtube support only?
33038             cfg.cn[0].cn.cn.push({
33039                 tag: 'iframe',
33040                 cls: 'masonry-brick-image-view',
33041                 src: vurl,
33042                 frameborder : 0,
33043                 allowfullscreen : true
33044             });
33045         }
33046         
33047         return cfg;
33048     },
33049     
33050     initEvents: function() 
33051     {
33052         switch (this.size) {
33053             case 'xs' :
33054                 this.x = 1;
33055                 this.y = 1;
33056                 break;
33057             case 'sm' :
33058                 this.x = 2;
33059                 this.y = 2;
33060                 break;
33061             case 'md' :
33062             case 'md-left' :
33063             case 'md-right' :
33064                 this.x = 3;
33065                 this.y = 3;
33066                 break;
33067             case 'tall' :
33068                 this.x = 2;
33069                 this.y = 3;
33070                 break;
33071             case 'wide' :
33072                 this.x = 3;
33073                 this.y = 2;
33074                 break;
33075             case 'wide-thin' :
33076                 this.x = 3;
33077                 this.y = 1;
33078                 break;
33079                         
33080             default :
33081                 break;
33082         }
33083         
33084         if(Roo.isTouch){
33085             this.el.on('touchstart', this.onTouchStart, this);
33086             this.el.on('touchmove', this.onTouchMove, this);
33087             this.el.on('touchend', this.onTouchEnd, this);
33088             this.el.on('contextmenu', this.onContextMenu, this);
33089         } else {
33090             this.el.on('mouseenter'  ,this.enter, this);
33091             this.el.on('mouseleave', this.leave, this);
33092             this.el.on('click', this.onClick, this);
33093         }
33094         
33095         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33096             this.parent().bricks.push(this);   
33097         }
33098         
33099     },
33100     
33101     onClick: function(e, el)
33102     {
33103         var time = this.endTimer - this.startTimer;
33104         // Roo.log(e.preventDefault());
33105         if(Roo.isTouch){
33106             if(time > 1000){
33107                 e.preventDefault();
33108                 return;
33109             }
33110         }
33111         
33112         if(!this.preventDefault){
33113             return;
33114         }
33115         
33116         e.preventDefault();
33117         
33118         if (this.activeClass != '') {
33119             this.selectBrick();
33120         }
33121         
33122         this.fireEvent('click', this, e);
33123     },
33124     
33125     enter: function(e, el)
33126     {
33127         e.preventDefault();
33128         
33129         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33130             return;
33131         }
33132         
33133         if(this.bgimage.length && this.html.length){
33134             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33135         }
33136     },
33137     
33138     leave: function(e, el)
33139     {
33140         e.preventDefault();
33141         
33142         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33143             return;
33144         }
33145         
33146         if(this.bgimage.length && this.html.length){
33147             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33148         }
33149     },
33150     
33151     onTouchStart: function(e, el)
33152     {
33153 //        e.preventDefault();
33154         
33155         this.touchmoved = false;
33156         
33157         if(!this.isFitContainer){
33158             return;
33159         }
33160         
33161         if(!this.bgimage.length || !this.html.length){
33162             return;
33163         }
33164         
33165         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33166         
33167         this.timer = new Date().getTime();
33168         
33169     },
33170     
33171     onTouchMove: function(e, el)
33172     {
33173         this.touchmoved = true;
33174     },
33175     
33176     onContextMenu : function(e,el)
33177     {
33178         e.preventDefault();
33179         e.stopPropagation();
33180         return false;
33181     },
33182     
33183     onTouchEnd: function(e, el)
33184     {
33185 //        e.preventDefault();
33186         
33187         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33188         
33189             this.leave(e,el);
33190             
33191             return;
33192         }
33193         
33194         if(!this.bgimage.length || !this.html.length){
33195             
33196             if(this.href.length){
33197                 window.location.href = this.href;
33198             }
33199             
33200             return;
33201         }
33202         
33203         if(!this.isFitContainer){
33204             return;
33205         }
33206         
33207         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33208         
33209         window.location.href = this.href;
33210     },
33211     
33212     //selection on single brick only
33213     selectBrick : function() {
33214         
33215         if (!this.parentId) {
33216             return;
33217         }
33218         
33219         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33220         var index = m.selectedBrick.indexOf(this.id);
33221         
33222         if ( index > -1) {
33223             m.selectedBrick.splice(index,1);
33224             this.el.removeClass(this.activeClass);
33225             return;
33226         }
33227         
33228         for(var i = 0; i < m.selectedBrick.length; i++) {
33229             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33230             b.el.removeClass(b.activeClass);
33231         }
33232         
33233         m.selectedBrick = [];
33234         
33235         m.selectedBrick.push(this.id);
33236         this.el.addClass(this.activeClass);
33237         return;
33238     },
33239     
33240     isSelected : function(){
33241         return this.el.hasClass(this.activeClass);
33242         
33243     }
33244 });
33245
33246 Roo.apply(Roo.bootstrap.MasonryBrick, {
33247     
33248     //groups: {},
33249     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33250      /**
33251     * register a Masonry Brick
33252     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33253     */
33254     
33255     register : function(brick)
33256     {
33257         //this.groups[brick.id] = brick;
33258         this.groups.add(brick.id, brick);
33259     },
33260     /**
33261     * fetch a  masonry brick based on the masonry brick ID
33262     * @param {string} the masonry brick to add
33263     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33264     */
33265     
33266     get: function(brick_id) 
33267     {
33268         // if (typeof(this.groups[brick_id]) == 'undefined') {
33269         //     return false;
33270         // }
33271         // return this.groups[brick_id] ;
33272         
33273         if(this.groups.key(brick_id)) {
33274             return this.groups.key(brick_id);
33275         }
33276         
33277         return false;
33278     }
33279     
33280     
33281     
33282 });
33283
33284  /*
33285  * - LGPL
33286  *
33287  * element
33288  * 
33289  */
33290
33291 /**
33292  * @class Roo.bootstrap.Brick
33293  * @extends Roo.bootstrap.Component
33294  * Bootstrap Brick class
33295  * 
33296  * @constructor
33297  * Create a new Brick
33298  * @param {Object} config The config object
33299  */
33300
33301 Roo.bootstrap.Brick = function(config){
33302     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33303     
33304     this.addEvents({
33305         // raw events
33306         /**
33307          * @event click
33308          * When a Brick is click
33309          * @param {Roo.bootstrap.Brick} this
33310          * @param {Roo.EventObject} e
33311          */
33312         "click" : true
33313     });
33314 };
33315
33316 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33317     
33318     /**
33319      * @cfg {String} title
33320      */   
33321     title : '',
33322     /**
33323      * @cfg {String} html
33324      */   
33325     html : '',
33326     /**
33327      * @cfg {String} bgimage
33328      */   
33329     bgimage : '',
33330     /**
33331      * @cfg {String} cls
33332      */   
33333     cls : '',
33334     /**
33335      * @cfg {String} href
33336      */   
33337     href : '',
33338     /**
33339      * @cfg {String} video
33340      */   
33341     video : '',
33342     /**
33343      * @cfg {Boolean} square
33344      */   
33345     square : true,
33346     
33347     getAutoCreate : function()
33348     {
33349         var cls = 'roo-brick';
33350         
33351         if(this.href.length){
33352             cls += ' roo-brick-link';
33353         }
33354         
33355         if(this.bgimage.length){
33356             cls += ' roo-brick-image';
33357         }
33358         
33359         if(!this.html.length && !this.bgimage.length){
33360             cls += ' roo-brick-center-title';
33361         }
33362         
33363         if(!this.html.length && this.bgimage.length){
33364             cls += ' roo-brick-bottom-title';
33365         }
33366         
33367         if(this.cls){
33368             cls += ' ' + this.cls;
33369         }
33370         
33371         var cfg = {
33372             tag: (this.href.length) ? 'a' : 'div',
33373             cls: cls,
33374             cn: [
33375                 {
33376                     tag: 'div',
33377                     cls: 'roo-brick-paragraph',
33378                     cn: []
33379                 }
33380             ]
33381         };
33382         
33383         if(this.href.length){
33384             cfg.href = this.href;
33385         }
33386         
33387         var cn = cfg.cn[0].cn;
33388         
33389         if(this.title.length){
33390             cn.push({
33391                 tag: 'h4',
33392                 cls: 'roo-brick-title',
33393                 html: this.title
33394             });
33395         }
33396         
33397         if(this.html.length){
33398             cn.push({
33399                 tag: 'p',
33400                 cls: 'roo-brick-text',
33401                 html: this.html
33402             });
33403         } else {
33404             cn.cls += ' hide';
33405         }
33406         
33407         if(this.bgimage.length){
33408             cfg.cn.push({
33409                 tag: 'img',
33410                 cls: 'roo-brick-image-view',
33411                 src: this.bgimage
33412             });
33413         }
33414         
33415         return cfg;
33416     },
33417     
33418     initEvents: function() 
33419     {
33420         if(this.title.length || this.html.length){
33421             this.el.on('mouseenter'  ,this.enter, this);
33422             this.el.on('mouseleave', this.leave, this);
33423         }
33424         
33425         Roo.EventManager.onWindowResize(this.resize, this); 
33426         
33427         if(this.bgimage.length){
33428             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33429             this.imageEl.on('load', this.onImageLoad, this);
33430             return;
33431         }
33432         
33433         this.resize();
33434     },
33435     
33436     onImageLoad : function()
33437     {
33438         this.resize();
33439     },
33440     
33441     resize : function()
33442     {
33443         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33444         
33445         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33446         
33447         if(this.bgimage.length){
33448             var image = this.el.select('.roo-brick-image-view', true).first();
33449             
33450             image.setWidth(paragraph.getWidth());
33451             
33452             if(this.square){
33453                 image.setHeight(paragraph.getWidth());
33454             }
33455             
33456             this.el.setHeight(image.getHeight());
33457             paragraph.setHeight(image.getHeight());
33458             
33459         }
33460         
33461     },
33462     
33463     enter: function(e, el)
33464     {
33465         e.preventDefault();
33466         
33467         if(this.bgimage.length){
33468             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33469             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33470         }
33471     },
33472     
33473     leave: function(e, el)
33474     {
33475         e.preventDefault();
33476         
33477         if(this.bgimage.length){
33478             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33479             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33480         }
33481     }
33482     
33483 });
33484
33485  
33486
33487  /*
33488  * - LGPL
33489  *
33490  * Number field 
33491  */
33492
33493 /**
33494  * @class Roo.bootstrap.NumberField
33495  * @extends Roo.bootstrap.Input
33496  * Bootstrap NumberField class
33497  * 
33498  * 
33499  * 
33500  * 
33501  * @constructor
33502  * Create a new NumberField
33503  * @param {Object} config The config object
33504  */
33505
33506 Roo.bootstrap.NumberField = function(config){
33507     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33508 };
33509
33510 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33511     
33512     /**
33513      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33514      */
33515     allowDecimals : true,
33516     /**
33517      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33518      */
33519     decimalSeparator : ".",
33520     /**
33521      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33522      */
33523     decimalPrecision : 2,
33524     /**
33525      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33526      */
33527     allowNegative : true,
33528     
33529     /**
33530      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33531      */
33532     allowZero: true,
33533     /**
33534      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33535      */
33536     minValue : Number.NEGATIVE_INFINITY,
33537     /**
33538      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33539      */
33540     maxValue : Number.MAX_VALUE,
33541     /**
33542      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33543      */
33544     minText : "The minimum value for this field is {0}",
33545     /**
33546      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33547      */
33548     maxText : "The maximum value for this field is {0}",
33549     /**
33550      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33551      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33552      */
33553     nanText : "{0} is not a valid number",
33554     /**
33555      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33556      */
33557     thousandsDelimiter : false,
33558     /**
33559      * @cfg {String} valueAlign alignment of value
33560      */
33561     valueAlign : "left",
33562
33563     getAutoCreate : function()
33564     {
33565         var hiddenInput = {
33566             tag: 'input',
33567             type: 'hidden',
33568             id: Roo.id(),
33569             cls: 'hidden-number-input'
33570         };
33571         
33572         if (this.name) {
33573             hiddenInput.name = this.name;
33574         }
33575         
33576         this.name = '';
33577         
33578         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33579         
33580         this.name = hiddenInput.name;
33581         
33582         if(cfg.cn.length > 0) {
33583             cfg.cn.push(hiddenInput);
33584         }
33585         
33586         return cfg;
33587     },
33588
33589     // private
33590     initEvents : function()
33591     {   
33592         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33593         
33594         var allowed = "0123456789";
33595         
33596         if(this.allowDecimals){
33597             allowed += this.decimalSeparator;
33598         }
33599         
33600         if(this.allowNegative){
33601             allowed += "-";
33602         }
33603         
33604         if(this.thousandsDelimiter) {
33605             allowed += ",";
33606         }
33607         
33608         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33609         
33610         var keyPress = function(e){
33611             
33612             var k = e.getKey();
33613             
33614             var c = e.getCharCode();
33615             
33616             if(
33617                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33618                     allowed.indexOf(String.fromCharCode(c)) === -1
33619             ){
33620                 e.stopEvent();
33621                 return;
33622             }
33623             
33624             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33625                 return;
33626             }
33627             
33628             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33629                 e.stopEvent();
33630             }
33631         };
33632         
33633         this.el.on("keypress", keyPress, this);
33634     },
33635     
33636     validateValue : function(value)
33637     {
33638         
33639         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33640             return false;
33641         }
33642         
33643         var num = this.parseValue(value);
33644         
33645         if(isNaN(num)){
33646             this.markInvalid(String.format(this.nanText, value));
33647             return false;
33648         }
33649         
33650         if(num < this.minValue){
33651             this.markInvalid(String.format(this.minText, this.minValue));
33652             return false;
33653         }
33654         
33655         if(num > this.maxValue){
33656             this.markInvalid(String.format(this.maxText, this.maxValue));
33657             return false;
33658         }
33659         
33660         return true;
33661     },
33662
33663     getValue : function()
33664     {
33665         var v = this.hiddenEl().getValue();
33666         
33667         return this.fixPrecision(this.parseValue(v));
33668     },
33669
33670     parseValue : function(value)
33671     {
33672         if(this.thousandsDelimiter) {
33673             value += "";
33674             r = new RegExp(",", "g");
33675             value = value.replace(r, "");
33676         }
33677         
33678         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33679         return isNaN(value) ? '' : value;
33680     },
33681
33682     fixPrecision : function(value)
33683     {
33684         if(this.thousandsDelimiter) {
33685             value += "";
33686             r = new RegExp(",", "g");
33687             value = value.replace(r, "");
33688         }
33689         
33690         var nan = isNaN(value);
33691         
33692         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33693             return nan ? '' : value;
33694         }
33695         return parseFloat(value).toFixed(this.decimalPrecision);
33696     },
33697
33698     setValue : function(v)
33699     {
33700         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33701         
33702         this.value = v;
33703         
33704         if(this.rendered){
33705             
33706             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33707             
33708             this.inputEl().dom.value = (v == '') ? '' :
33709                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33710             
33711             if(!this.allowZero && v === '0') {
33712                 this.hiddenEl().dom.value = '';
33713                 this.inputEl().dom.value = '';
33714             }
33715             
33716             this.validate();
33717         }
33718     },
33719
33720     decimalPrecisionFcn : function(v)
33721     {
33722         return Math.floor(v);
33723     },
33724
33725     beforeBlur : function()
33726     {
33727         var v = this.parseValue(this.getRawValue());
33728         
33729         if(v || v === 0 || v === ''){
33730             this.setValue(v);
33731         }
33732     },
33733     
33734     hiddenEl : function()
33735     {
33736         return this.el.select('input.hidden-number-input',true).first();
33737     }
33738     
33739 });
33740
33741  
33742
33743 /*
33744 * Licence: LGPL
33745 */
33746
33747 /**
33748  * @class Roo.bootstrap.DocumentSlider
33749  * @extends Roo.bootstrap.Component
33750  * Bootstrap DocumentSlider class
33751  * 
33752  * @constructor
33753  * Create a new DocumentViewer
33754  * @param {Object} config The config object
33755  */
33756
33757 Roo.bootstrap.DocumentSlider = function(config){
33758     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33759     
33760     this.files = [];
33761     
33762     this.addEvents({
33763         /**
33764          * @event initial
33765          * Fire after initEvent
33766          * @param {Roo.bootstrap.DocumentSlider} this
33767          */
33768         "initial" : true,
33769         /**
33770          * @event update
33771          * Fire after update
33772          * @param {Roo.bootstrap.DocumentSlider} this
33773          */
33774         "update" : true,
33775         /**
33776          * @event click
33777          * Fire after click
33778          * @param {Roo.bootstrap.DocumentSlider} this
33779          */
33780         "click" : true
33781     });
33782 };
33783
33784 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33785     
33786     files : false,
33787     
33788     indicator : 0,
33789     
33790     getAutoCreate : function()
33791     {
33792         var cfg = {
33793             tag : 'div',
33794             cls : 'roo-document-slider',
33795             cn : [
33796                 {
33797                     tag : 'div',
33798                     cls : 'roo-document-slider-header',
33799                     cn : [
33800                         {
33801                             tag : 'div',
33802                             cls : 'roo-document-slider-header-title'
33803                         }
33804                     ]
33805                 },
33806                 {
33807                     tag : 'div',
33808                     cls : 'roo-document-slider-body',
33809                     cn : [
33810                         {
33811                             tag : 'div',
33812                             cls : 'roo-document-slider-prev',
33813                             cn : [
33814                                 {
33815                                     tag : 'i',
33816                                     cls : 'fa fa-chevron-left'
33817                                 }
33818                             ]
33819                         },
33820                         {
33821                             tag : 'div',
33822                             cls : 'roo-document-slider-thumb',
33823                             cn : [
33824                                 {
33825                                     tag : 'img',
33826                                     cls : 'roo-document-slider-image'
33827                                 }
33828                             ]
33829                         },
33830                         {
33831                             tag : 'div',
33832                             cls : 'roo-document-slider-next',
33833                             cn : [
33834                                 {
33835                                     tag : 'i',
33836                                     cls : 'fa fa-chevron-right'
33837                                 }
33838                             ]
33839                         }
33840                     ]
33841                 }
33842             ]
33843         };
33844         
33845         return cfg;
33846     },
33847     
33848     initEvents : function()
33849     {
33850         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33851         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33852         
33853         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33854         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33855         
33856         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33857         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33858         
33859         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33860         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33861         
33862         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33863         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33864         
33865         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33866         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33867         
33868         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33869         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33870         
33871         this.thumbEl.on('click', this.onClick, this);
33872         
33873         this.prevIndicator.on('click', this.prev, this);
33874         
33875         this.nextIndicator.on('click', this.next, this);
33876         
33877     },
33878     
33879     initial : function()
33880     {
33881         if(this.files.length){
33882             this.indicator = 1;
33883             this.update()
33884         }
33885         
33886         this.fireEvent('initial', this);
33887     },
33888     
33889     update : function()
33890     {
33891         this.imageEl.attr('src', this.files[this.indicator - 1]);
33892         
33893         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33894         
33895         this.prevIndicator.show();
33896         
33897         if(this.indicator == 1){
33898             this.prevIndicator.hide();
33899         }
33900         
33901         this.nextIndicator.show();
33902         
33903         if(this.indicator == this.files.length){
33904             this.nextIndicator.hide();
33905         }
33906         
33907         this.thumbEl.scrollTo('top');
33908         
33909         this.fireEvent('update', this);
33910     },
33911     
33912     onClick : function(e)
33913     {
33914         e.preventDefault();
33915         
33916         this.fireEvent('click', this);
33917     },
33918     
33919     prev : function(e)
33920     {
33921         e.preventDefault();
33922         
33923         this.indicator = Math.max(1, this.indicator - 1);
33924         
33925         this.update();
33926     },
33927     
33928     next : function(e)
33929     {
33930         e.preventDefault();
33931         
33932         this.indicator = Math.min(this.files.length, this.indicator + 1);
33933         
33934         this.update();
33935     }
33936 });
33937 /*
33938  * - LGPL
33939  *
33940  * RadioSet
33941  *
33942  *
33943  */
33944
33945 /**
33946  * @class Roo.bootstrap.RadioSet
33947  * @extends Roo.bootstrap.Input
33948  * Bootstrap RadioSet class
33949  * @cfg {String} indicatorpos (left|right) default left
33950  * @cfg {Boolean} inline (true|false) inline the element (default true)
33951  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33952  * @constructor
33953  * Create a new RadioSet
33954  * @param {Object} config The config object
33955  */
33956
33957 Roo.bootstrap.RadioSet = function(config){
33958     
33959     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33960     
33961     this.radioes = [];
33962     
33963     Roo.bootstrap.RadioSet.register(this);
33964     
33965     this.addEvents({
33966         /**
33967         * @event check
33968         * Fires when the element is checked or unchecked.
33969         * @param {Roo.bootstrap.RadioSet} this This radio
33970         * @param {Roo.bootstrap.Radio} item The checked item
33971         */
33972        check : true,
33973        /**
33974         * @event click
33975         * Fires when the element is click.
33976         * @param {Roo.bootstrap.RadioSet} this This radio set
33977         * @param {Roo.bootstrap.Radio} item The checked item
33978         * @param {Roo.EventObject} e The event object
33979         */
33980        click : true
33981     });
33982     
33983 };
33984
33985 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33986
33987     radioes : false,
33988     
33989     inline : true,
33990     
33991     weight : '',
33992     
33993     indicatorpos : 'left',
33994     
33995     getAutoCreate : function()
33996     {
33997         var label = {
33998             tag : 'label',
33999             cls : 'roo-radio-set-label',
34000             cn : [
34001                 {
34002                     tag : 'span',
34003                     html : this.fieldLabel
34004                 }
34005             ]
34006         };
34007         
34008         if(this.indicatorpos == 'left'){
34009             label.cn.unshift({
34010                 tag : 'i',
34011                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34012                 tooltip : 'This field is required'
34013             });
34014         } else {
34015             label.cn.push({
34016                 tag : 'i',
34017                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34018                 tooltip : 'This field is required'
34019             });
34020         }
34021         
34022         var items = {
34023             tag : 'div',
34024             cls : 'roo-radio-set-items'
34025         };
34026         
34027         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34028         
34029         if (align === 'left' && this.fieldLabel.length) {
34030             
34031             items = {
34032                 cls : "roo-radio-set-right", 
34033                 cn: [
34034                     items
34035                 ]
34036             };
34037             
34038             if(this.labelWidth > 12){
34039                 label.style = "width: " + this.labelWidth + 'px';
34040             }
34041             
34042             if(this.labelWidth < 13 && this.labelmd == 0){
34043                 this.labelmd = this.labelWidth;
34044             }
34045             
34046             if(this.labellg > 0){
34047                 label.cls += ' col-lg-' + this.labellg;
34048                 items.cls += ' col-lg-' + (12 - this.labellg);
34049             }
34050             
34051             if(this.labelmd > 0){
34052                 label.cls += ' col-md-' + this.labelmd;
34053                 items.cls += ' col-md-' + (12 - this.labelmd);
34054             }
34055             
34056             if(this.labelsm > 0){
34057                 label.cls += ' col-sm-' + this.labelsm;
34058                 items.cls += ' col-sm-' + (12 - this.labelsm);
34059             }
34060             
34061             if(this.labelxs > 0){
34062                 label.cls += ' col-xs-' + this.labelxs;
34063                 items.cls += ' col-xs-' + (12 - this.labelxs);
34064             }
34065         }
34066         
34067         var cfg = {
34068             tag : 'div',
34069             cls : 'roo-radio-set',
34070             cn : [
34071                 {
34072                     tag : 'input',
34073                     cls : 'roo-radio-set-input',
34074                     type : 'hidden',
34075                     name : this.name,
34076                     value : this.value ? this.value :  ''
34077                 },
34078                 label,
34079                 items
34080             ]
34081         };
34082         
34083         if(this.weight.length){
34084             cfg.cls += ' roo-radio-' + this.weight;
34085         }
34086         
34087         if(this.inline) {
34088             cfg.cls += ' roo-radio-set-inline';
34089         }
34090         
34091         var settings=this;
34092         ['xs','sm','md','lg'].map(function(size){
34093             if (settings[size]) {
34094                 cfg.cls += ' col-' + size + '-' + settings[size];
34095             }
34096         });
34097         
34098         return cfg;
34099         
34100     },
34101
34102     initEvents : function()
34103     {
34104         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34105         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34106         
34107         if(!this.fieldLabel.length){
34108             this.labelEl.hide();
34109         }
34110         
34111         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34112         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34113         
34114         this.indicator = this.indicatorEl();
34115         
34116         if(this.indicator){
34117             this.indicator.addClass('invisible');
34118         }
34119         
34120         this.originalValue = this.getValue();
34121         
34122     },
34123     
34124     inputEl: function ()
34125     {
34126         return this.el.select('.roo-radio-set-input', true).first();
34127     },
34128     
34129     getChildContainer : function()
34130     {
34131         return this.itemsEl;
34132     },
34133     
34134     register : function(item)
34135     {
34136         this.radioes.push(item);
34137         
34138     },
34139     
34140     validate : function()
34141     {   
34142         if(this.getVisibilityEl().hasClass('hidden')){
34143             return true;
34144         }
34145         
34146         var valid = false;
34147         
34148         Roo.each(this.radioes, function(i){
34149             if(!i.checked){
34150                 return;
34151             }
34152             
34153             valid = true;
34154             return false;
34155         });
34156         
34157         if(this.allowBlank) {
34158             return true;
34159         }
34160         
34161         if(this.disabled || valid){
34162             this.markValid();
34163             return true;
34164         }
34165         
34166         this.markInvalid();
34167         return false;
34168         
34169     },
34170     
34171     markValid : function()
34172     {
34173         if(this.labelEl.isVisible(true)){
34174             this.indicatorEl().removeClass('visible');
34175             this.indicatorEl().addClass('invisible');
34176         }
34177         
34178         this.el.removeClass([this.invalidClass, this.validClass]);
34179         this.el.addClass(this.validClass);
34180         
34181         this.fireEvent('valid', this);
34182     },
34183     
34184     markInvalid : function(msg)
34185     {
34186         if(this.allowBlank || this.disabled){
34187             return;
34188         }
34189         
34190         if(this.labelEl.isVisible(true)){
34191             this.indicatorEl().removeClass('invisible');
34192             this.indicatorEl().addClass('visible');
34193         }
34194         
34195         this.el.removeClass([this.invalidClass, this.validClass]);
34196         this.el.addClass(this.invalidClass);
34197         
34198         this.fireEvent('invalid', this, msg);
34199         
34200     },
34201     
34202     setValue : function(v, suppressEvent)
34203     {   
34204         if(this.value === v){
34205             return;
34206         }
34207         
34208         this.value = v;
34209         
34210         if(this.rendered){
34211             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34212         }
34213         
34214         Roo.each(this.radioes, function(i){
34215             i.checked = false;
34216             i.el.removeClass('checked');
34217         });
34218         
34219         Roo.each(this.radioes, function(i){
34220             
34221             if(i.value === v || i.value.toString() === v.toString()){
34222                 i.checked = true;
34223                 i.el.addClass('checked');
34224                 
34225                 if(suppressEvent !== true){
34226                     this.fireEvent('check', this, i);
34227                 }
34228                 
34229                 return false;
34230             }
34231             
34232         }, this);
34233         
34234         this.validate();
34235     },
34236     
34237     clearInvalid : function(){
34238         
34239         if(!this.el || this.preventMark){
34240             return;
34241         }
34242         
34243         this.el.removeClass([this.invalidClass]);
34244         
34245         this.fireEvent('valid', this);
34246     }
34247     
34248 });
34249
34250 Roo.apply(Roo.bootstrap.RadioSet, {
34251     
34252     groups: {},
34253     
34254     register : function(set)
34255     {
34256         this.groups[set.name] = set;
34257     },
34258     
34259     get: function(name) 
34260     {
34261         if (typeof(this.groups[name]) == 'undefined') {
34262             return false;
34263         }
34264         
34265         return this.groups[name] ;
34266     }
34267     
34268 });
34269 /*
34270  * Based on:
34271  * Ext JS Library 1.1.1
34272  * Copyright(c) 2006-2007, Ext JS, LLC.
34273  *
34274  * Originally Released Under LGPL - original licence link has changed is not relivant.
34275  *
34276  * Fork - LGPL
34277  * <script type="text/javascript">
34278  */
34279
34280
34281 /**
34282  * @class Roo.bootstrap.SplitBar
34283  * @extends Roo.util.Observable
34284  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34285  * <br><br>
34286  * Usage:
34287  * <pre><code>
34288 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34289                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34290 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34291 split.minSize = 100;
34292 split.maxSize = 600;
34293 split.animate = true;
34294 split.on('moved', splitterMoved);
34295 </code></pre>
34296  * @constructor
34297  * Create a new SplitBar
34298  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34299  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34300  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34301  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34302                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34303                         position of the SplitBar).
34304  */
34305 Roo.bootstrap.SplitBar = function(cfg){
34306     
34307     /** @private */
34308     
34309     //{
34310     //  dragElement : elm
34311     //  resizingElement: el,
34312         // optional..
34313     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34314     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34315         // existingProxy ???
34316     //}
34317     
34318     this.el = Roo.get(cfg.dragElement, true);
34319     this.el.dom.unselectable = "on";
34320     /** @private */
34321     this.resizingEl = Roo.get(cfg.resizingElement, true);
34322
34323     /**
34324      * @private
34325      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34326      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34327      * @type Number
34328      */
34329     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34330     
34331     /**
34332      * The minimum size of the resizing element. (Defaults to 0)
34333      * @type Number
34334      */
34335     this.minSize = 0;
34336     
34337     /**
34338      * The maximum size of the resizing element. (Defaults to 2000)
34339      * @type Number
34340      */
34341     this.maxSize = 2000;
34342     
34343     /**
34344      * Whether to animate the transition to the new size
34345      * @type Boolean
34346      */
34347     this.animate = false;
34348     
34349     /**
34350      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34351      * @type Boolean
34352      */
34353     this.useShim = false;
34354     
34355     /** @private */
34356     this.shim = null;
34357     
34358     if(!cfg.existingProxy){
34359         /** @private */
34360         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34361     }else{
34362         this.proxy = Roo.get(cfg.existingProxy).dom;
34363     }
34364     /** @private */
34365     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34366     
34367     /** @private */
34368     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34369     
34370     /** @private */
34371     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34372     
34373     /** @private */
34374     this.dragSpecs = {};
34375     
34376     /**
34377      * @private The adapter to use to positon and resize elements
34378      */
34379     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34380     this.adapter.init(this);
34381     
34382     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34383         /** @private */
34384         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34385         this.el.addClass("roo-splitbar-h");
34386     }else{
34387         /** @private */
34388         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34389         this.el.addClass("roo-splitbar-v");
34390     }
34391     
34392     this.addEvents({
34393         /**
34394          * @event resize
34395          * Fires when the splitter is moved (alias for {@link #event-moved})
34396          * @param {Roo.bootstrap.SplitBar} this
34397          * @param {Number} newSize the new width or height
34398          */
34399         "resize" : true,
34400         /**
34401          * @event moved
34402          * Fires when the splitter is moved
34403          * @param {Roo.bootstrap.SplitBar} this
34404          * @param {Number} newSize the new width or height
34405          */
34406         "moved" : true,
34407         /**
34408          * @event beforeresize
34409          * Fires before the splitter is dragged
34410          * @param {Roo.bootstrap.SplitBar} this
34411          */
34412         "beforeresize" : true,
34413
34414         "beforeapply" : true
34415     });
34416
34417     Roo.util.Observable.call(this);
34418 };
34419
34420 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34421     onStartProxyDrag : function(x, y){
34422         this.fireEvent("beforeresize", this);
34423         if(!this.overlay){
34424             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34425             o.unselectable();
34426             o.enableDisplayMode("block");
34427             // all splitbars share the same overlay
34428             Roo.bootstrap.SplitBar.prototype.overlay = o;
34429         }
34430         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34431         this.overlay.show();
34432         Roo.get(this.proxy).setDisplayed("block");
34433         var size = this.adapter.getElementSize(this);
34434         this.activeMinSize = this.getMinimumSize();;
34435         this.activeMaxSize = this.getMaximumSize();;
34436         var c1 = size - this.activeMinSize;
34437         var c2 = Math.max(this.activeMaxSize - size, 0);
34438         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34439             this.dd.resetConstraints();
34440             this.dd.setXConstraint(
34441                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34442                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34443             );
34444             this.dd.setYConstraint(0, 0);
34445         }else{
34446             this.dd.resetConstraints();
34447             this.dd.setXConstraint(0, 0);
34448             this.dd.setYConstraint(
34449                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34450                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34451             );
34452          }
34453         this.dragSpecs.startSize = size;
34454         this.dragSpecs.startPoint = [x, y];
34455         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34456     },
34457     
34458     /** 
34459      * @private Called after the drag operation by the DDProxy
34460      */
34461     onEndProxyDrag : function(e){
34462         Roo.get(this.proxy).setDisplayed(false);
34463         var endPoint = Roo.lib.Event.getXY(e);
34464         if(this.overlay){
34465             this.overlay.hide();
34466         }
34467         var newSize;
34468         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34469             newSize = this.dragSpecs.startSize + 
34470                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34471                     endPoint[0] - this.dragSpecs.startPoint[0] :
34472                     this.dragSpecs.startPoint[0] - endPoint[0]
34473                 );
34474         }else{
34475             newSize = this.dragSpecs.startSize + 
34476                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34477                     endPoint[1] - this.dragSpecs.startPoint[1] :
34478                     this.dragSpecs.startPoint[1] - endPoint[1]
34479                 );
34480         }
34481         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34482         if(newSize != this.dragSpecs.startSize){
34483             if(this.fireEvent('beforeapply', this, newSize) !== false){
34484                 this.adapter.setElementSize(this, newSize);
34485                 this.fireEvent("moved", this, newSize);
34486                 this.fireEvent("resize", this, newSize);
34487             }
34488         }
34489     },
34490     
34491     /**
34492      * Get the adapter this SplitBar uses
34493      * @return The adapter object
34494      */
34495     getAdapter : function(){
34496         return this.adapter;
34497     },
34498     
34499     /**
34500      * Set the adapter this SplitBar uses
34501      * @param {Object} adapter A SplitBar adapter object
34502      */
34503     setAdapter : function(adapter){
34504         this.adapter = adapter;
34505         this.adapter.init(this);
34506     },
34507     
34508     /**
34509      * Gets the minimum size for the resizing element
34510      * @return {Number} The minimum size
34511      */
34512     getMinimumSize : function(){
34513         return this.minSize;
34514     },
34515     
34516     /**
34517      * Sets the minimum size for the resizing element
34518      * @param {Number} minSize The minimum size
34519      */
34520     setMinimumSize : function(minSize){
34521         this.minSize = minSize;
34522     },
34523     
34524     /**
34525      * Gets the maximum size for the resizing element
34526      * @return {Number} The maximum size
34527      */
34528     getMaximumSize : function(){
34529         return this.maxSize;
34530     },
34531     
34532     /**
34533      * Sets the maximum size for the resizing element
34534      * @param {Number} maxSize The maximum size
34535      */
34536     setMaximumSize : function(maxSize){
34537         this.maxSize = maxSize;
34538     },
34539     
34540     /**
34541      * Sets the initialize size for the resizing element
34542      * @param {Number} size The initial size
34543      */
34544     setCurrentSize : function(size){
34545         var oldAnimate = this.animate;
34546         this.animate = false;
34547         this.adapter.setElementSize(this, size);
34548         this.animate = oldAnimate;
34549     },
34550     
34551     /**
34552      * Destroy this splitbar. 
34553      * @param {Boolean} removeEl True to remove the element
34554      */
34555     destroy : function(removeEl){
34556         if(this.shim){
34557             this.shim.remove();
34558         }
34559         this.dd.unreg();
34560         this.proxy.parentNode.removeChild(this.proxy);
34561         if(removeEl){
34562             this.el.remove();
34563         }
34564     }
34565 });
34566
34567 /**
34568  * @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.
34569  */
34570 Roo.bootstrap.SplitBar.createProxy = function(dir){
34571     var proxy = new Roo.Element(document.createElement("div"));
34572     proxy.unselectable();
34573     var cls = 'roo-splitbar-proxy';
34574     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34575     document.body.appendChild(proxy.dom);
34576     return proxy.dom;
34577 };
34578
34579 /** 
34580  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34581  * Default Adapter. It assumes the splitter and resizing element are not positioned
34582  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34583  */
34584 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34585 };
34586
34587 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34588     // do nothing for now
34589     init : function(s){
34590     
34591     },
34592     /**
34593      * Called before drag operations to get the current size of the resizing element. 
34594      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34595      */
34596      getElementSize : function(s){
34597         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34598             return s.resizingEl.getWidth();
34599         }else{
34600             return s.resizingEl.getHeight();
34601         }
34602     },
34603     
34604     /**
34605      * Called after drag operations to set the size of the resizing element.
34606      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34607      * @param {Number} newSize The new size to set
34608      * @param {Function} onComplete A function to be invoked when resizing is complete
34609      */
34610     setElementSize : function(s, newSize, onComplete){
34611         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34612             if(!s.animate){
34613                 s.resizingEl.setWidth(newSize);
34614                 if(onComplete){
34615                     onComplete(s, newSize);
34616                 }
34617             }else{
34618                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34619             }
34620         }else{
34621             
34622             if(!s.animate){
34623                 s.resizingEl.setHeight(newSize);
34624                 if(onComplete){
34625                     onComplete(s, newSize);
34626                 }
34627             }else{
34628                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34629             }
34630         }
34631     }
34632 };
34633
34634 /** 
34635  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34636  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34637  * Adapter that  moves the splitter element to align with the resized sizing element. 
34638  * Used with an absolute positioned SplitBar.
34639  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34640  * document.body, make sure you assign an id to the body element.
34641  */
34642 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34643     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34644     this.container = Roo.get(container);
34645 };
34646
34647 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34648     init : function(s){
34649         this.basic.init(s);
34650     },
34651     
34652     getElementSize : function(s){
34653         return this.basic.getElementSize(s);
34654     },
34655     
34656     setElementSize : function(s, newSize, onComplete){
34657         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34658     },
34659     
34660     moveSplitter : function(s){
34661         var yes = Roo.bootstrap.SplitBar;
34662         switch(s.placement){
34663             case yes.LEFT:
34664                 s.el.setX(s.resizingEl.getRight());
34665                 break;
34666             case yes.RIGHT:
34667                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34668                 break;
34669             case yes.TOP:
34670                 s.el.setY(s.resizingEl.getBottom());
34671                 break;
34672             case yes.BOTTOM:
34673                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34674                 break;
34675         }
34676     }
34677 };
34678
34679 /**
34680  * Orientation constant - Create a vertical SplitBar
34681  * @static
34682  * @type Number
34683  */
34684 Roo.bootstrap.SplitBar.VERTICAL = 1;
34685
34686 /**
34687  * Orientation constant - Create a horizontal SplitBar
34688  * @static
34689  * @type Number
34690  */
34691 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34692
34693 /**
34694  * Placement constant - The resizing element is to the left of the splitter element
34695  * @static
34696  * @type Number
34697  */
34698 Roo.bootstrap.SplitBar.LEFT = 1;
34699
34700 /**
34701  * Placement constant - The resizing element is to the right of the splitter element
34702  * @static
34703  * @type Number
34704  */
34705 Roo.bootstrap.SplitBar.RIGHT = 2;
34706
34707 /**
34708  * Placement constant - The resizing element is positioned above the splitter element
34709  * @static
34710  * @type Number
34711  */
34712 Roo.bootstrap.SplitBar.TOP = 3;
34713
34714 /**
34715  * Placement constant - The resizing element is positioned under splitter element
34716  * @static
34717  * @type Number
34718  */
34719 Roo.bootstrap.SplitBar.BOTTOM = 4;
34720 Roo.namespace("Roo.bootstrap.layout");/*
34721  * Based on:
34722  * Ext JS Library 1.1.1
34723  * Copyright(c) 2006-2007, Ext JS, LLC.
34724  *
34725  * Originally Released Under LGPL - original licence link has changed is not relivant.
34726  *
34727  * Fork - LGPL
34728  * <script type="text/javascript">
34729  */
34730
34731 /**
34732  * @class Roo.bootstrap.layout.Manager
34733  * @extends Roo.bootstrap.Component
34734  * Base class for layout managers.
34735  */
34736 Roo.bootstrap.layout.Manager = function(config)
34737 {
34738     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34739
34740
34741
34742
34743
34744     /** false to disable window resize monitoring @type Boolean */
34745     this.monitorWindowResize = true;
34746     this.regions = {};
34747     this.addEvents({
34748         /**
34749          * @event layout
34750          * Fires when a layout is performed.
34751          * @param {Roo.LayoutManager} this
34752          */
34753         "layout" : true,
34754         /**
34755          * @event regionresized
34756          * Fires when the user resizes a region.
34757          * @param {Roo.LayoutRegion} region The resized region
34758          * @param {Number} newSize The new size (width for east/west, height for north/south)
34759          */
34760         "regionresized" : true,
34761         /**
34762          * @event regioncollapsed
34763          * Fires when a region is collapsed.
34764          * @param {Roo.LayoutRegion} region The collapsed region
34765          */
34766         "regioncollapsed" : true,
34767         /**
34768          * @event regionexpanded
34769          * Fires when a region is expanded.
34770          * @param {Roo.LayoutRegion} region The expanded region
34771          */
34772         "regionexpanded" : true
34773     });
34774     this.updating = false;
34775
34776     if (config.el) {
34777         this.el = Roo.get(config.el);
34778         this.initEvents();
34779     }
34780
34781 };
34782
34783 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34784
34785
34786     regions : null,
34787
34788     monitorWindowResize : true,
34789
34790
34791     updating : false,
34792
34793
34794     onRender : function(ct, position)
34795     {
34796         if(!this.el){
34797             this.el = Roo.get(ct);
34798             this.initEvents();
34799         }
34800         //this.fireEvent('render',this);
34801     },
34802
34803
34804     initEvents: function()
34805     {
34806
34807
34808         // ie scrollbar fix
34809         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34810             document.body.scroll = "no";
34811         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34812             this.el.position('relative');
34813         }
34814         this.id = this.el.id;
34815         this.el.addClass("roo-layout-container");
34816         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34817         if(this.el.dom != document.body ) {
34818             this.el.on('resize', this.layout,this);
34819             this.el.on('show', this.layout,this);
34820         }
34821
34822     },
34823
34824     /**
34825      * Returns true if this layout is currently being updated
34826      * @return {Boolean}
34827      */
34828     isUpdating : function(){
34829         return this.updating;
34830     },
34831
34832     /**
34833      * Suspend the LayoutManager from doing auto-layouts while
34834      * making multiple add or remove calls
34835      */
34836     beginUpdate : function(){
34837         this.updating = true;
34838     },
34839
34840     /**
34841      * Restore auto-layouts and optionally disable the manager from performing a layout
34842      * @param {Boolean} noLayout true to disable a layout update
34843      */
34844     endUpdate : function(noLayout){
34845         this.updating = false;
34846         if(!noLayout){
34847             this.layout();
34848         }
34849     },
34850
34851     layout: function(){
34852         // abstract...
34853     },
34854
34855     onRegionResized : function(region, newSize){
34856         this.fireEvent("regionresized", region, newSize);
34857         this.layout();
34858     },
34859
34860     onRegionCollapsed : function(region){
34861         this.fireEvent("regioncollapsed", region);
34862     },
34863
34864     onRegionExpanded : function(region){
34865         this.fireEvent("regionexpanded", region);
34866     },
34867
34868     /**
34869      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34870      * performs box-model adjustments.
34871      * @return {Object} The size as an object {width: (the width), height: (the height)}
34872      */
34873     getViewSize : function()
34874     {
34875         var size;
34876         if(this.el.dom != document.body){
34877             size = this.el.getSize();
34878         }else{
34879             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34880         }
34881         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34882         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34883         return size;
34884     },
34885
34886     /**
34887      * Returns the Element this layout is bound to.
34888      * @return {Roo.Element}
34889      */
34890     getEl : function(){
34891         return this.el;
34892     },
34893
34894     /**
34895      * Returns the specified region.
34896      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34897      * @return {Roo.LayoutRegion}
34898      */
34899     getRegion : function(target){
34900         return this.regions[target.toLowerCase()];
34901     },
34902
34903     onWindowResize : function(){
34904         if(this.monitorWindowResize){
34905             this.layout();
34906         }
34907     }
34908 });
34909 /*
34910  * Based on:
34911  * Ext JS Library 1.1.1
34912  * Copyright(c) 2006-2007, Ext JS, LLC.
34913  *
34914  * Originally Released Under LGPL - original licence link has changed is not relivant.
34915  *
34916  * Fork - LGPL
34917  * <script type="text/javascript">
34918  */
34919 /**
34920  * @class Roo.bootstrap.layout.Border
34921  * @extends Roo.bootstrap.layout.Manager
34922  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34923  * please see: examples/bootstrap/nested.html<br><br>
34924  
34925 <b>The container the layout is rendered into can be either the body element or any other element.
34926 If it is not the body element, the container needs to either be an absolute positioned element,
34927 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34928 the container size if it is not the body element.</b>
34929
34930 * @constructor
34931 * Create a new Border
34932 * @param {Object} config Configuration options
34933  */
34934 Roo.bootstrap.layout.Border = function(config){
34935     config = config || {};
34936     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34937     
34938     
34939     
34940     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34941         if(config[region]){
34942             config[region].region = region;
34943             this.addRegion(config[region]);
34944         }
34945     },this);
34946     
34947 };
34948
34949 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34950
34951 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34952     /**
34953      * Creates and adds a new region if it doesn't already exist.
34954      * @param {String} target The target region key (north, south, east, west or center).
34955      * @param {Object} config The regions config object
34956      * @return {BorderLayoutRegion} The new region
34957      */
34958     addRegion : function(config)
34959     {
34960         if(!this.regions[config.region]){
34961             var r = this.factory(config);
34962             this.bindRegion(r);
34963         }
34964         return this.regions[config.region];
34965     },
34966
34967     // private (kinda)
34968     bindRegion : function(r){
34969         this.regions[r.config.region] = r;
34970         
34971         r.on("visibilitychange",    this.layout, this);
34972         r.on("paneladded",          this.layout, this);
34973         r.on("panelremoved",        this.layout, this);
34974         r.on("invalidated",         this.layout, this);
34975         r.on("resized",             this.onRegionResized, this);
34976         r.on("collapsed",           this.onRegionCollapsed, this);
34977         r.on("expanded",            this.onRegionExpanded, this);
34978     },
34979
34980     /**
34981      * Performs a layout update.
34982      */
34983     layout : function()
34984     {
34985         if(this.updating) {
34986             return;
34987         }
34988         
34989         // render all the rebions if they have not been done alreayd?
34990         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34991             if(this.regions[region] && !this.regions[region].bodyEl){
34992                 this.regions[region].onRender(this.el)
34993             }
34994         },this);
34995         
34996         var size = this.getViewSize();
34997         var w = size.width;
34998         var h = size.height;
34999         var centerW = w;
35000         var centerH = h;
35001         var centerY = 0;
35002         var centerX = 0;
35003         //var x = 0, y = 0;
35004
35005         var rs = this.regions;
35006         var north = rs["north"];
35007         var south = rs["south"]; 
35008         var west = rs["west"];
35009         var east = rs["east"];
35010         var center = rs["center"];
35011         //if(this.hideOnLayout){ // not supported anymore
35012             //c.el.setStyle("display", "none");
35013         //}
35014         if(north && north.isVisible()){
35015             var b = north.getBox();
35016             var m = north.getMargins();
35017             b.width = w - (m.left+m.right);
35018             b.x = m.left;
35019             b.y = m.top;
35020             centerY = b.height + b.y + m.bottom;
35021             centerH -= centerY;
35022             north.updateBox(this.safeBox(b));
35023         }
35024         if(south && south.isVisible()){
35025             var b = south.getBox();
35026             var m = south.getMargins();
35027             b.width = w - (m.left+m.right);
35028             b.x = m.left;
35029             var totalHeight = (b.height + m.top + m.bottom);
35030             b.y = h - totalHeight + m.top;
35031             centerH -= totalHeight;
35032             south.updateBox(this.safeBox(b));
35033         }
35034         if(west && west.isVisible()){
35035             var b = west.getBox();
35036             var m = west.getMargins();
35037             b.height = centerH - (m.top+m.bottom);
35038             b.x = m.left;
35039             b.y = centerY + m.top;
35040             var totalWidth = (b.width + m.left + m.right);
35041             centerX += totalWidth;
35042             centerW -= totalWidth;
35043             west.updateBox(this.safeBox(b));
35044         }
35045         if(east && east.isVisible()){
35046             var b = east.getBox();
35047             var m = east.getMargins();
35048             b.height = centerH - (m.top+m.bottom);
35049             var totalWidth = (b.width + m.left + m.right);
35050             b.x = w - totalWidth + m.left;
35051             b.y = centerY + m.top;
35052             centerW -= totalWidth;
35053             east.updateBox(this.safeBox(b));
35054         }
35055         if(center){
35056             var m = center.getMargins();
35057             var centerBox = {
35058                 x: centerX + m.left,
35059                 y: centerY + m.top,
35060                 width: centerW - (m.left+m.right),
35061                 height: centerH - (m.top+m.bottom)
35062             };
35063             //if(this.hideOnLayout){
35064                 //center.el.setStyle("display", "block");
35065             //}
35066             center.updateBox(this.safeBox(centerBox));
35067         }
35068         this.el.repaint();
35069         this.fireEvent("layout", this);
35070     },
35071
35072     // private
35073     safeBox : function(box){
35074         box.width = Math.max(0, box.width);
35075         box.height = Math.max(0, box.height);
35076         return box;
35077     },
35078
35079     /**
35080      * Adds a ContentPanel (or subclass) to this layout.
35081      * @param {String} target The target region key (north, south, east, west or center).
35082      * @param {Roo.ContentPanel} panel The panel to add
35083      * @return {Roo.ContentPanel} The added panel
35084      */
35085     add : function(target, panel){
35086          
35087         target = target.toLowerCase();
35088         return this.regions[target].add(panel);
35089     },
35090
35091     /**
35092      * Remove a ContentPanel (or subclass) to this layout.
35093      * @param {String} target The target region key (north, south, east, west or center).
35094      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35095      * @return {Roo.ContentPanel} The removed panel
35096      */
35097     remove : function(target, panel){
35098         target = target.toLowerCase();
35099         return this.regions[target].remove(panel);
35100     },
35101
35102     /**
35103      * Searches all regions for a panel with the specified id
35104      * @param {String} panelId
35105      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35106      */
35107     findPanel : function(panelId){
35108         var rs = this.regions;
35109         for(var target in rs){
35110             if(typeof rs[target] != "function"){
35111                 var p = rs[target].getPanel(panelId);
35112                 if(p){
35113                     return p;
35114                 }
35115             }
35116         }
35117         return null;
35118     },
35119
35120     /**
35121      * Searches all regions for a panel with the specified id and activates (shows) it.
35122      * @param {String/ContentPanel} panelId The panels id or the panel itself
35123      * @return {Roo.ContentPanel} The shown panel or null
35124      */
35125     showPanel : function(panelId) {
35126       var rs = this.regions;
35127       for(var target in rs){
35128          var r = rs[target];
35129          if(typeof r != "function"){
35130             if(r.hasPanel(panelId)){
35131                return r.showPanel(panelId);
35132             }
35133          }
35134       }
35135       return null;
35136    },
35137
35138    /**
35139      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35140      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35141      */
35142    /*
35143     restoreState : function(provider){
35144         if(!provider){
35145             provider = Roo.state.Manager;
35146         }
35147         var sm = new Roo.LayoutStateManager();
35148         sm.init(this, provider);
35149     },
35150 */
35151  
35152  
35153     /**
35154      * Adds a xtype elements to the layout.
35155      * <pre><code>
35156
35157 layout.addxtype({
35158        xtype : 'ContentPanel',
35159        region: 'west',
35160        items: [ .... ]
35161    }
35162 );
35163
35164 layout.addxtype({
35165         xtype : 'NestedLayoutPanel',
35166         region: 'west',
35167         layout: {
35168            center: { },
35169            west: { }   
35170         },
35171         items : [ ... list of content panels or nested layout panels.. ]
35172    }
35173 );
35174 </code></pre>
35175      * @param {Object} cfg Xtype definition of item to add.
35176      */
35177     addxtype : function(cfg)
35178     {
35179         // basically accepts a pannel...
35180         // can accept a layout region..!?!?
35181         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35182         
35183         
35184         // theory?  children can only be panels??
35185         
35186         //if (!cfg.xtype.match(/Panel$/)) {
35187         //    return false;
35188         //}
35189         var ret = false;
35190         
35191         if (typeof(cfg.region) == 'undefined') {
35192             Roo.log("Failed to add Panel, region was not set");
35193             Roo.log(cfg);
35194             return false;
35195         }
35196         var region = cfg.region;
35197         delete cfg.region;
35198         
35199           
35200         var xitems = [];
35201         if (cfg.items) {
35202             xitems = cfg.items;
35203             delete cfg.items;
35204         }
35205         var nb = false;
35206         
35207         switch(cfg.xtype) 
35208         {
35209             case 'Content':  // ContentPanel (el, cfg)
35210             case 'Scroll':  // ContentPanel (el, cfg)
35211             case 'View': 
35212                 cfg.autoCreate = true;
35213                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35214                 //} else {
35215                 //    var el = this.el.createChild();
35216                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35217                 //}
35218                 
35219                 this.add(region, ret);
35220                 break;
35221             
35222             /*
35223             case 'TreePanel': // our new panel!
35224                 cfg.el = this.el.createChild();
35225                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35226                 this.add(region, ret);
35227                 break;
35228             */
35229             
35230             case 'Nest': 
35231                 // create a new Layout (which is  a Border Layout...
35232                 
35233                 var clayout = cfg.layout;
35234                 clayout.el  = this.el.createChild();
35235                 clayout.items   = clayout.items  || [];
35236                 
35237                 delete cfg.layout;
35238                 
35239                 // replace this exitems with the clayout ones..
35240                 xitems = clayout.items;
35241                  
35242                 // force background off if it's in center...
35243                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35244                     cfg.background = false;
35245                 }
35246                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35247                 
35248                 
35249                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35250                 //console.log('adding nested layout panel '  + cfg.toSource());
35251                 this.add(region, ret);
35252                 nb = {}; /// find first...
35253                 break;
35254             
35255             case 'Grid':
35256                 
35257                 // needs grid and region
35258                 
35259                 //var el = this.getRegion(region).el.createChild();
35260                 /*
35261                  *var el = this.el.createChild();
35262                 // create the grid first...
35263                 cfg.grid.container = el;
35264                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35265                 */
35266                 
35267                 if (region == 'center' && this.active ) {
35268                     cfg.background = false;
35269                 }
35270                 
35271                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35272                 
35273                 this.add(region, ret);
35274                 /*
35275                 if (cfg.background) {
35276                     // render grid on panel activation (if panel background)
35277                     ret.on('activate', function(gp) {
35278                         if (!gp.grid.rendered) {
35279                     //        gp.grid.render(el);
35280                         }
35281                     });
35282                 } else {
35283                   //  cfg.grid.render(el);
35284                 }
35285                 */
35286                 break;
35287            
35288            
35289             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35290                 // it was the old xcomponent building that caused this before.
35291                 // espeically if border is the top element in the tree.
35292                 ret = this;
35293                 break; 
35294                 
35295                     
35296                 
35297                 
35298                 
35299             default:
35300                 /*
35301                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35302                     
35303                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35304                     this.add(region, ret);
35305                 } else {
35306                 */
35307                     Roo.log(cfg);
35308                     throw "Can not add '" + cfg.xtype + "' to Border";
35309                     return null;
35310              
35311                                 
35312              
35313         }
35314         this.beginUpdate();
35315         // add children..
35316         var region = '';
35317         var abn = {};
35318         Roo.each(xitems, function(i)  {
35319             region = nb && i.region ? i.region : false;
35320             
35321             var add = ret.addxtype(i);
35322            
35323             if (region) {
35324                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35325                 if (!i.background) {
35326                     abn[region] = nb[region] ;
35327                 }
35328             }
35329             
35330         });
35331         this.endUpdate();
35332
35333         // make the last non-background panel active..
35334         //if (nb) { Roo.log(abn); }
35335         if (nb) {
35336             
35337             for(var r in abn) {
35338                 region = this.getRegion(r);
35339                 if (region) {
35340                     // tried using nb[r], but it does not work..
35341                      
35342                     region.showPanel(abn[r]);
35343                    
35344                 }
35345             }
35346         }
35347         return ret;
35348         
35349     },
35350     
35351     
35352 // private
35353     factory : function(cfg)
35354     {
35355         
35356         var validRegions = Roo.bootstrap.layout.Border.regions;
35357
35358         var target = cfg.region;
35359         cfg.mgr = this;
35360         
35361         var r = Roo.bootstrap.layout;
35362         Roo.log(target);
35363         switch(target){
35364             case "north":
35365                 return new r.North(cfg);
35366             case "south":
35367                 return new r.South(cfg);
35368             case "east":
35369                 return new r.East(cfg);
35370             case "west":
35371                 return new r.West(cfg);
35372             case "center":
35373                 return new r.Center(cfg);
35374         }
35375         throw 'Layout region "'+target+'" not supported.';
35376     }
35377     
35378     
35379 });
35380  /*
35381  * Based on:
35382  * Ext JS Library 1.1.1
35383  * Copyright(c) 2006-2007, Ext JS, LLC.
35384  *
35385  * Originally Released Under LGPL - original licence link has changed is not relivant.
35386  *
35387  * Fork - LGPL
35388  * <script type="text/javascript">
35389  */
35390  
35391 /**
35392  * @class Roo.bootstrap.layout.Basic
35393  * @extends Roo.util.Observable
35394  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35395  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35396  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35397  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35398  * @cfg {string}   region  the region that it inhabits..
35399  * @cfg {bool}   skipConfig skip config?
35400  * 
35401
35402  */
35403 Roo.bootstrap.layout.Basic = function(config){
35404     
35405     this.mgr = config.mgr;
35406     
35407     this.position = config.region;
35408     
35409     var skipConfig = config.skipConfig;
35410     
35411     this.events = {
35412         /**
35413          * @scope Roo.BasicLayoutRegion
35414          */
35415         
35416         /**
35417          * @event beforeremove
35418          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35419          * @param {Roo.LayoutRegion} this
35420          * @param {Roo.ContentPanel} panel The panel
35421          * @param {Object} e The cancel event object
35422          */
35423         "beforeremove" : true,
35424         /**
35425          * @event invalidated
35426          * Fires when the layout for this region is changed.
35427          * @param {Roo.LayoutRegion} this
35428          */
35429         "invalidated" : true,
35430         /**
35431          * @event visibilitychange
35432          * Fires when this region is shown or hidden 
35433          * @param {Roo.LayoutRegion} this
35434          * @param {Boolean} visibility true or false
35435          */
35436         "visibilitychange" : true,
35437         /**
35438          * @event paneladded
35439          * Fires when a panel is added. 
35440          * @param {Roo.LayoutRegion} this
35441          * @param {Roo.ContentPanel} panel The panel
35442          */
35443         "paneladded" : true,
35444         /**
35445          * @event panelremoved
35446          * Fires when a panel is removed. 
35447          * @param {Roo.LayoutRegion} this
35448          * @param {Roo.ContentPanel} panel The panel
35449          */
35450         "panelremoved" : true,
35451         /**
35452          * @event beforecollapse
35453          * Fires when this region before collapse.
35454          * @param {Roo.LayoutRegion} this
35455          */
35456         "beforecollapse" : true,
35457         /**
35458          * @event collapsed
35459          * Fires when this region is collapsed.
35460          * @param {Roo.LayoutRegion} this
35461          */
35462         "collapsed" : true,
35463         /**
35464          * @event expanded
35465          * Fires when this region is expanded.
35466          * @param {Roo.LayoutRegion} this
35467          */
35468         "expanded" : true,
35469         /**
35470          * @event slideshow
35471          * Fires when this region is slid into view.
35472          * @param {Roo.LayoutRegion} this
35473          */
35474         "slideshow" : true,
35475         /**
35476          * @event slidehide
35477          * Fires when this region slides out of view. 
35478          * @param {Roo.LayoutRegion} this
35479          */
35480         "slidehide" : true,
35481         /**
35482          * @event panelactivated
35483          * Fires when a panel is activated. 
35484          * @param {Roo.LayoutRegion} this
35485          * @param {Roo.ContentPanel} panel The activated panel
35486          */
35487         "panelactivated" : true,
35488         /**
35489          * @event resized
35490          * Fires when the user resizes this region. 
35491          * @param {Roo.LayoutRegion} this
35492          * @param {Number} newSize The new size (width for east/west, height for north/south)
35493          */
35494         "resized" : true
35495     };
35496     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35497     this.panels = new Roo.util.MixedCollection();
35498     this.panels.getKey = this.getPanelId.createDelegate(this);
35499     this.box = null;
35500     this.activePanel = null;
35501     // ensure listeners are added...
35502     
35503     if (config.listeners || config.events) {
35504         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35505             listeners : config.listeners || {},
35506             events : config.events || {}
35507         });
35508     }
35509     
35510     if(skipConfig !== true){
35511         this.applyConfig(config);
35512     }
35513 };
35514
35515 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35516 {
35517     getPanelId : function(p){
35518         return p.getId();
35519     },
35520     
35521     applyConfig : function(config){
35522         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35523         this.config = config;
35524         
35525     },
35526     
35527     /**
35528      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35529      * the width, for horizontal (north, south) the height.
35530      * @param {Number} newSize The new width or height
35531      */
35532     resizeTo : function(newSize){
35533         var el = this.el ? this.el :
35534                  (this.activePanel ? this.activePanel.getEl() : null);
35535         if(el){
35536             switch(this.position){
35537                 case "east":
35538                 case "west":
35539                     el.setWidth(newSize);
35540                     this.fireEvent("resized", this, newSize);
35541                 break;
35542                 case "north":
35543                 case "south":
35544                     el.setHeight(newSize);
35545                     this.fireEvent("resized", this, newSize);
35546                 break;                
35547             }
35548         }
35549     },
35550     
35551     getBox : function(){
35552         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35553     },
35554     
35555     getMargins : function(){
35556         return this.margins;
35557     },
35558     
35559     updateBox : function(box){
35560         this.box = box;
35561         var el = this.activePanel.getEl();
35562         el.dom.style.left = box.x + "px";
35563         el.dom.style.top = box.y + "px";
35564         this.activePanel.setSize(box.width, box.height);
35565     },
35566     
35567     /**
35568      * Returns the container element for this region.
35569      * @return {Roo.Element}
35570      */
35571     getEl : function(){
35572         return this.activePanel;
35573     },
35574     
35575     /**
35576      * Returns true if this region is currently visible.
35577      * @return {Boolean}
35578      */
35579     isVisible : function(){
35580         return this.activePanel ? true : false;
35581     },
35582     
35583     setActivePanel : function(panel){
35584         panel = this.getPanel(panel);
35585         if(this.activePanel && this.activePanel != panel){
35586             this.activePanel.setActiveState(false);
35587             this.activePanel.getEl().setLeftTop(-10000,-10000);
35588         }
35589         this.activePanel = panel;
35590         panel.setActiveState(true);
35591         if(this.box){
35592             panel.setSize(this.box.width, this.box.height);
35593         }
35594         this.fireEvent("panelactivated", this, panel);
35595         this.fireEvent("invalidated");
35596     },
35597     
35598     /**
35599      * Show the specified panel.
35600      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35601      * @return {Roo.ContentPanel} The shown panel or null
35602      */
35603     showPanel : function(panel){
35604         panel = this.getPanel(panel);
35605         if(panel){
35606             this.setActivePanel(panel);
35607         }
35608         return panel;
35609     },
35610     
35611     /**
35612      * Get the active panel for this region.
35613      * @return {Roo.ContentPanel} The active panel or null
35614      */
35615     getActivePanel : function(){
35616         return this.activePanel;
35617     },
35618     
35619     /**
35620      * Add the passed ContentPanel(s)
35621      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35622      * @return {Roo.ContentPanel} The panel added (if only one was added)
35623      */
35624     add : function(panel){
35625         if(arguments.length > 1){
35626             for(var i = 0, len = arguments.length; i < len; i++) {
35627                 this.add(arguments[i]);
35628             }
35629             return null;
35630         }
35631         if(this.hasPanel(panel)){
35632             this.showPanel(panel);
35633             return panel;
35634         }
35635         var el = panel.getEl();
35636         if(el.dom.parentNode != this.mgr.el.dom){
35637             this.mgr.el.dom.appendChild(el.dom);
35638         }
35639         if(panel.setRegion){
35640             panel.setRegion(this);
35641         }
35642         this.panels.add(panel);
35643         el.setStyle("position", "absolute");
35644         if(!panel.background){
35645             this.setActivePanel(panel);
35646             if(this.config.initialSize && this.panels.getCount()==1){
35647                 this.resizeTo(this.config.initialSize);
35648             }
35649         }
35650         this.fireEvent("paneladded", this, panel);
35651         return panel;
35652     },
35653     
35654     /**
35655      * Returns true if the panel is in this region.
35656      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35657      * @return {Boolean}
35658      */
35659     hasPanel : function(panel){
35660         if(typeof panel == "object"){ // must be panel obj
35661             panel = panel.getId();
35662         }
35663         return this.getPanel(panel) ? true : false;
35664     },
35665     
35666     /**
35667      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35668      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35669      * @param {Boolean} preservePanel Overrides the config preservePanel option
35670      * @return {Roo.ContentPanel} The panel that was removed
35671      */
35672     remove : function(panel, preservePanel){
35673         panel = this.getPanel(panel);
35674         if(!panel){
35675             return null;
35676         }
35677         var e = {};
35678         this.fireEvent("beforeremove", this, panel, e);
35679         if(e.cancel === true){
35680             return null;
35681         }
35682         var panelId = panel.getId();
35683         this.panels.removeKey(panelId);
35684         return panel;
35685     },
35686     
35687     /**
35688      * Returns the panel specified or null if it's not in this region.
35689      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35690      * @return {Roo.ContentPanel}
35691      */
35692     getPanel : function(id){
35693         if(typeof id == "object"){ // must be panel obj
35694             return id;
35695         }
35696         return this.panels.get(id);
35697     },
35698     
35699     /**
35700      * Returns this regions position (north/south/east/west/center).
35701      * @return {String} 
35702      */
35703     getPosition: function(){
35704         return this.position;    
35705     }
35706 });/*
35707  * Based on:
35708  * Ext JS Library 1.1.1
35709  * Copyright(c) 2006-2007, Ext JS, LLC.
35710  *
35711  * Originally Released Under LGPL - original licence link has changed is not relivant.
35712  *
35713  * Fork - LGPL
35714  * <script type="text/javascript">
35715  */
35716  
35717 /**
35718  * @class Roo.bootstrap.layout.Region
35719  * @extends Roo.bootstrap.layout.Basic
35720  * This class represents a region in a layout manager.
35721  
35722  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35723  * @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})
35724  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35725  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35726  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35727  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35728  * @cfg {String}    title           The title for the region (overrides panel titles)
35729  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35730  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35731  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35732  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35733  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35734  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35735  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35736  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35737  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35738  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35739
35740  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35741  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35742  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35743  * @cfg {Number}    width           For East/West panels
35744  * @cfg {Number}    height          For North/South panels
35745  * @cfg {Boolean}   split           To show the splitter
35746  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35747  * 
35748  * @cfg {string}   cls             Extra CSS classes to add to region
35749  * 
35750  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35751  * @cfg {string}   region  the region that it inhabits..
35752  *
35753
35754  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35755  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35756
35757  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35758  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35759  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35760  */
35761 Roo.bootstrap.layout.Region = function(config)
35762 {
35763     this.applyConfig(config);
35764
35765     var mgr = config.mgr;
35766     var pos = config.region;
35767     config.skipConfig = true;
35768     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35769     
35770     if (mgr.el) {
35771         this.onRender(mgr.el);   
35772     }
35773      
35774     this.visible = true;
35775     this.collapsed = false;
35776     this.unrendered_panels = [];
35777 };
35778
35779 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35780
35781     position: '', // set by wrapper (eg. north/south etc..)
35782     unrendered_panels : null,  // unrendered panels.
35783     createBody : function(){
35784         /** This region's body element 
35785         * @type Roo.Element */
35786         this.bodyEl = this.el.createChild({
35787                 tag: "div",
35788                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35789         });
35790     },
35791
35792     onRender: function(ctr, pos)
35793     {
35794         var dh = Roo.DomHelper;
35795         /** This region's container element 
35796         * @type Roo.Element */
35797         this.el = dh.append(ctr.dom, {
35798                 tag: "div",
35799                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35800             }, true);
35801         /** This region's title element 
35802         * @type Roo.Element */
35803     
35804         this.titleEl = dh.append(this.el.dom,
35805             {
35806                     tag: "div",
35807                     unselectable: "on",
35808                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35809                     children:[
35810                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35811                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35812                     ]}, true);
35813         
35814         this.titleEl.enableDisplayMode();
35815         /** This region's title text element 
35816         * @type HTMLElement */
35817         this.titleTextEl = this.titleEl.dom.firstChild;
35818         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35819         /*
35820         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35821         this.closeBtn.enableDisplayMode();
35822         this.closeBtn.on("click", this.closeClicked, this);
35823         this.closeBtn.hide();
35824     */
35825         this.createBody(this.config);
35826         if(this.config.hideWhenEmpty){
35827             this.hide();
35828             this.on("paneladded", this.validateVisibility, this);
35829             this.on("panelremoved", this.validateVisibility, this);
35830         }
35831         if(this.autoScroll){
35832             this.bodyEl.setStyle("overflow", "auto");
35833         }else{
35834             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35835         }
35836         //if(c.titlebar !== false){
35837             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35838                 this.titleEl.hide();
35839             }else{
35840                 this.titleEl.show();
35841                 if(this.config.title){
35842                     this.titleTextEl.innerHTML = this.config.title;
35843                 }
35844             }
35845         //}
35846         if(this.config.collapsed){
35847             this.collapse(true);
35848         }
35849         if(this.config.hidden){
35850             this.hide();
35851         }
35852         
35853         if (this.unrendered_panels && this.unrendered_panels.length) {
35854             for (var i =0;i< this.unrendered_panels.length; i++) {
35855                 this.add(this.unrendered_panels[i]);
35856             }
35857             this.unrendered_panels = null;
35858             
35859         }
35860         
35861     },
35862     
35863     applyConfig : function(c)
35864     {
35865         /*
35866          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35867             var dh = Roo.DomHelper;
35868             if(c.titlebar !== false){
35869                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35870                 this.collapseBtn.on("click", this.collapse, this);
35871                 this.collapseBtn.enableDisplayMode();
35872                 /*
35873                 if(c.showPin === true || this.showPin){
35874                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35875                     this.stickBtn.enableDisplayMode();
35876                     this.stickBtn.on("click", this.expand, this);
35877                     this.stickBtn.hide();
35878                 }
35879                 
35880             }
35881             */
35882             /** This region's collapsed element
35883             * @type Roo.Element */
35884             /*
35885              *
35886             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35887                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35888             ]}, true);
35889             
35890             if(c.floatable !== false){
35891                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35892                this.collapsedEl.on("click", this.collapseClick, this);
35893             }
35894
35895             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35896                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35897                    id: "message", unselectable: "on", style:{"float":"left"}});
35898                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35899              }
35900             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35901             this.expandBtn.on("click", this.expand, this);
35902             
35903         }
35904         
35905         if(this.collapseBtn){
35906             this.collapseBtn.setVisible(c.collapsible == true);
35907         }
35908         
35909         this.cmargins = c.cmargins || this.cmargins ||
35910                          (this.position == "west" || this.position == "east" ?
35911                              {top: 0, left: 2, right:2, bottom: 0} :
35912                              {top: 2, left: 0, right:0, bottom: 2});
35913         */
35914         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35915         
35916         
35917         this.bottomTabs = c.tabPosition != "top";
35918         
35919         this.autoScroll = c.autoScroll || false;
35920         
35921         
35922        
35923         
35924         this.duration = c.duration || .30;
35925         this.slideDuration = c.slideDuration || .45;
35926         this.config = c;
35927        
35928     },
35929     /**
35930      * Returns true if this region is currently visible.
35931      * @return {Boolean}
35932      */
35933     isVisible : function(){
35934         return this.visible;
35935     },
35936
35937     /**
35938      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35939      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35940      */
35941     //setCollapsedTitle : function(title){
35942     //    title = title || "&#160;";
35943      //   if(this.collapsedTitleTextEl){
35944       //      this.collapsedTitleTextEl.innerHTML = title;
35945        // }
35946     //},
35947
35948     getBox : function(){
35949         var b;
35950       //  if(!this.collapsed){
35951             b = this.el.getBox(false, true);
35952        // }else{
35953           //  b = this.collapsedEl.getBox(false, true);
35954         //}
35955         return b;
35956     },
35957
35958     getMargins : function(){
35959         return this.margins;
35960         //return this.collapsed ? this.cmargins : this.margins;
35961     },
35962 /*
35963     highlight : function(){
35964         this.el.addClass("x-layout-panel-dragover");
35965     },
35966
35967     unhighlight : function(){
35968         this.el.removeClass("x-layout-panel-dragover");
35969     },
35970 */
35971     updateBox : function(box)
35972     {
35973         if (!this.bodyEl) {
35974             return; // not rendered yet..
35975         }
35976         
35977         this.box = box;
35978         if(!this.collapsed){
35979             this.el.dom.style.left = box.x + "px";
35980             this.el.dom.style.top = box.y + "px";
35981             this.updateBody(box.width, box.height);
35982         }else{
35983             this.collapsedEl.dom.style.left = box.x + "px";
35984             this.collapsedEl.dom.style.top = box.y + "px";
35985             this.collapsedEl.setSize(box.width, box.height);
35986         }
35987         if(this.tabs){
35988             this.tabs.autoSizeTabs();
35989         }
35990     },
35991
35992     updateBody : function(w, h)
35993     {
35994         if(w !== null){
35995             this.el.setWidth(w);
35996             w -= this.el.getBorderWidth("rl");
35997             if(this.config.adjustments){
35998                 w += this.config.adjustments[0];
35999             }
36000         }
36001         if(h !== null && h > 0){
36002             this.el.setHeight(h);
36003             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36004             h -= this.el.getBorderWidth("tb");
36005             if(this.config.adjustments){
36006                 h += this.config.adjustments[1];
36007             }
36008             this.bodyEl.setHeight(h);
36009             if(this.tabs){
36010                 h = this.tabs.syncHeight(h);
36011             }
36012         }
36013         if(this.panelSize){
36014             w = w !== null ? w : this.panelSize.width;
36015             h = h !== null ? h : this.panelSize.height;
36016         }
36017         if(this.activePanel){
36018             var el = this.activePanel.getEl();
36019             w = w !== null ? w : el.getWidth();
36020             h = h !== null ? h : el.getHeight();
36021             this.panelSize = {width: w, height: h};
36022             this.activePanel.setSize(w, h);
36023         }
36024         if(Roo.isIE && this.tabs){
36025             this.tabs.el.repaint();
36026         }
36027     },
36028
36029     /**
36030      * Returns the container element for this region.
36031      * @return {Roo.Element}
36032      */
36033     getEl : function(){
36034         return this.el;
36035     },
36036
36037     /**
36038      * Hides this region.
36039      */
36040     hide : function(){
36041         //if(!this.collapsed){
36042             this.el.dom.style.left = "-2000px";
36043             this.el.hide();
36044         //}else{
36045          //   this.collapsedEl.dom.style.left = "-2000px";
36046          //   this.collapsedEl.hide();
36047        // }
36048         this.visible = false;
36049         this.fireEvent("visibilitychange", this, false);
36050     },
36051
36052     /**
36053      * Shows this region if it was previously hidden.
36054      */
36055     show : function(){
36056         //if(!this.collapsed){
36057             this.el.show();
36058         //}else{
36059         //    this.collapsedEl.show();
36060        // }
36061         this.visible = true;
36062         this.fireEvent("visibilitychange", this, true);
36063     },
36064 /*
36065     closeClicked : function(){
36066         if(this.activePanel){
36067             this.remove(this.activePanel);
36068         }
36069     },
36070
36071     collapseClick : function(e){
36072         if(this.isSlid){
36073            e.stopPropagation();
36074            this.slideIn();
36075         }else{
36076            e.stopPropagation();
36077            this.slideOut();
36078         }
36079     },
36080 */
36081     /**
36082      * Collapses this region.
36083      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36084      */
36085     /*
36086     collapse : function(skipAnim, skipCheck = false){
36087         if(this.collapsed) {
36088             return;
36089         }
36090         
36091         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36092             
36093             this.collapsed = true;
36094             if(this.split){
36095                 this.split.el.hide();
36096             }
36097             if(this.config.animate && skipAnim !== true){
36098                 this.fireEvent("invalidated", this);
36099                 this.animateCollapse();
36100             }else{
36101                 this.el.setLocation(-20000,-20000);
36102                 this.el.hide();
36103                 this.collapsedEl.show();
36104                 this.fireEvent("collapsed", this);
36105                 this.fireEvent("invalidated", this);
36106             }
36107         }
36108         
36109     },
36110 */
36111     animateCollapse : function(){
36112         // overridden
36113     },
36114
36115     /**
36116      * Expands this region if it was previously collapsed.
36117      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36118      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36119      */
36120     /*
36121     expand : function(e, skipAnim){
36122         if(e) {
36123             e.stopPropagation();
36124         }
36125         if(!this.collapsed || this.el.hasActiveFx()) {
36126             return;
36127         }
36128         if(this.isSlid){
36129             this.afterSlideIn();
36130             skipAnim = true;
36131         }
36132         this.collapsed = false;
36133         if(this.config.animate && skipAnim !== true){
36134             this.animateExpand();
36135         }else{
36136             this.el.show();
36137             if(this.split){
36138                 this.split.el.show();
36139             }
36140             this.collapsedEl.setLocation(-2000,-2000);
36141             this.collapsedEl.hide();
36142             this.fireEvent("invalidated", this);
36143             this.fireEvent("expanded", this);
36144         }
36145     },
36146 */
36147     animateExpand : function(){
36148         // overridden
36149     },
36150
36151     initTabs : function()
36152     {
36153         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36154         
36155         var ts = new Roo.bootstrap.panel.Tabs({
36156                 el: this.bodyEl.dom,
36157                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36158                 disableTooltips: this.config.disableTabTips,
36159                 toolbar : this.config.toolbar
36160             });
36161         
36162         if(this.config.hideTabs){
36163             ts.stripWrap.setDisplayed(false);
36164         }
36165         this.tabs = ts;
36166         ts.resizeTabs = this.config.resizeTabs === true;
36167         ts.minTabWidth = this.config.minTabWidth || 40;
36168         ts.maxTabWidth = this.config.maxTabWidth || 250;
36169         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36170         ts.monitorResize = false;
36171         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36172         ts.bodyEl.addClass('roo-layout-tabs-body');
36173         this.panels.each(this.initPanelAsTab, this);
36174     },
36175
36176     initPanelAsTab : function(panel){
36177         var ti = this.tabs.addTab(
36178             panel.getEl().id,
36179             panel.getTitle(),
36180             null,
36181             this.config.closeOnTab && panel.isClosable(),
36182             panel.tpl
36183         );
36184         if(panel.tabTip !== undefined){
36185             ti.setTooltip(panel.tabTip);
36186         }
36187         ti.on("activate", function(){
36188               this.setActivePanel(panel);
36189         }, this);
36190         
36191         if(this.config.closeOnTab){
36192             ti.on("beforeclose", function(t, e){
36193                 e.cancel = true;
36194                 this.remove(panel);
36195             }, this);
36196         }
36197         
36198         panel.tabItem = ti;
36199         
36200         return ti;
36201     },
36202
36203     updatePanelTitle : function(panel, title)
36204     {
36205         if(this.activePanel == panel){
36206             this.updateTitle(title);
36207         }
36208         if(this.tabs){
36209             var ti = this.tabs.getTab(panel.getEl().id);
36210             ti.setText(title);
36211             if(panel.tabTip !== undefined){
36212                 ti.setTooltip(panel.tabTip);
36213             }
36214         }
36215     },
36216
36217     updateTitle : function(title){
36218         if(this.titleTextEl && !this.config.title){
36219             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36220         }
36221     },
36222
36223     setActivePanel : function(panel)
36224     {
36225         panel = this.getPanel(panel);
36226         if(this.activePanel && this.activePanel != panel){
36227             if(this.activePanel.setActiveState(false) === false){
36228                 return;
36229             }
36230         }
36231         this.activePanel = panel;
36232         panel.setActiveState(true);
36233         if(this.panelSize){
36234             panel.setSize(this.panelSize.width, this.panelSize.height);
36235         }
36236         if(this.closeBtn){
36237             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36238         }
36239         this.updateTitle(panel.getTitle());
36240         if(this.tabs){
36241             this.fireEvent("invalidated", this);
36242         }
36243         this.fireEvent("panelactivated", this, panel);
36244     },
36245
36246     /**
36247      * Shows the specified panel.
36248      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36249      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36250      */
36251     showPanel : function(panel)
36252     {
36253         panel = this.getPanel(panel);
36254         if(panel){
36255             if(this.tabs){
36256                 var tab = this.tabs.getTab(panel.getEl().id);
36257                 if(tab.isHidden()){
36258                     this.tabs.unhideTab(tab.id);
36259                 }
36260                 tab.activate();
36261             }else{
36262                 this.setActivePanel(panel);
36263             }
36264         }
36265         return panel;
36266     },
36267
36268     /**
36269      * Get the active panel for this region.
36270      * @return {Roo.ContentPanel} The active panel or null
36271      */
36272     getActivePanel : function(){
36273         return this.activePanel;
36274     },
36275
36276     validateVisibility : function(){
36277         if(this.panels.getCount() < 1){
36278             this.updateTitle("&#160;");
36279             this.closeBtn.hide();
36280             this.hide();
36281         }else{
36282             if(!this.isVisible()){
36283                 this.show();
36284             }
36285         }
36286     },
36287
36288     /**
36289      * Adds the passed ContentPanel(s) to this region.
36290      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36291      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36292      */
36293     add : function(panel)
36294     {
36295         if(arguments.length > 1){
36296             for(var i = 0, len = arguments.length; i < len; i++) {
36297                 this.add(arguments[i]);
36298             }
36299             return null;
36300         }
36301         
36302         // if we have not been rendered yet, then we can not really do much of this..
36303         if (!this.bodyEl) {
36304             this.unrendered_panels.push(panel);
36305             return panel;
36306         }
36307         
36308         
36309         
36310         
36311         if(this.hasPanel(panel)){
36312             this.showPanel(panel);
36313             return panel;
36314         }
36315         panel.setRegion(this);
36316         this.panels.add(panel);
36317        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36318             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36319             // and hide them... ???
36320             this.bodyEl.dom.appendChild(panel.getEl().dom);
36321             if(panel.background !== true){
36322                 this.setActivePanel(panel);
36323             }
36324             this.fireEvent("paneladded", this, panel);
36325             return panel;
36326         }
36327         */
36328         if(!this.tabs){
36329             this.initTabs();
36330         }else{
36331             this.initPanelAsTab(panel);
36332         }
36333         
36334         
36335         if(panel.background !== true){
36336             this.tabs.activate(panel.getEl().id);
36337         }
36338         this.fireEvent("paneladded", this, panel);
36339         return panel;
36340     },
36341
36342     /**
36343      * Hides the tab for the specified panel.
36344      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36345      */
36346     hidePanel : function(panel){
36347         if(this.tabs && (panel = this.getPanel(panel))){
36348             this.tabs.hideTab(panel.getEl().id);
36349         }
36350     },
36351
36352     /**
36353      * Unhides the tab for a previously hidden panel.
36354      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36355      */
36356     unhidePanel : function(panel){
36357         if(this.tabs && (panel = this.getPanel(panel))){
36358             this.tabs.unhideTab(panel.getEl().id);
36359         }
36360     },
36361
36362     clearPanels : function(){
36363         while(this.panels.getCount() > 0){
36364              this.remove(this.panels.first());
36365         }
36366     },
36367
36368     /**
36369      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36370      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36371      * @param {Boolean} preservePanel Overrides the config preservePanel option
36372      * @return {Roo.ContentPanel} The panel that was removed
36373      */
36374     remove : function(panel, preservePanel)
36375     {
36376         panel = this.getPanel(panel);
36377         if(!panel){
36378             return null;
36379         }
36380         var e = {};
36381         this.fireEvent("beforeremove", this, panel, e);
36382         if(e.cancel === true){
36383             return null;
36384         }
36385         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36386         var panelId = panel.getId();
36387         this.panels.removeKey(panelId);
36388         if(preservePanel){
36389             document.body.appendChild(panel.getEl().dom);
36390         }
36391         if(this.tabs){
36392             this.tabs.removeTab(panel.getEl().id);
36393         }else if (!preservePanel){
36394             this.bodyEl.dom.removeChild(panel.getEl().dom);
36395         }
36396         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36397             var p = this.panels.first();
36398             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36399             tempEl.appendChild(p.getEl().dom);
36400             this.bodyEl.update("");
36401             this.bodyEl.dom.appendChild(p.getEl().dom);
36402             tempEl = null;
36403             this.updateTitle(p.getTitle());
36404             this.tabs = null;
36405             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36406             this.setActivePanel(p);
36407         }
36408         panel.setRegion(null);
36409         if(this.activePanel == panel){
36410             this.activePanel = null;
36411         }
36412         if(this.config.autoDestroy !== false && preservePanel !== true){
36413             try{panel.destroy();}catch(e){}
36414         }
36415         this.fireEvent("panelremoved", this, panel);
36416         return panel;
36417     },
36418
36419     /**
36420      * Returns the TabPanel component used by this region
36421      * @return {Roo.TabPanel}
36422      */
36423     getTabs : function(){
36424         return this.tabs;
36425     },
36426
36427     createTool : function(parentEl, className){
36428         var btn = Roo.DomHelper.append(parentEl, {
36429             tag: "div",
36430             cls: "x-layout-tools-button",
36431             children: [ {
36432                 tag: "div",
36433                 cls: "roo-layout-tools-button-inner " + className,
36434                 html: "&#160;"
36435             }]
36436         }, true);
36437         btn.addClassOnOver("roo-layout-tools-button-over");
36438         return btn;
36439     }
36440 });/*
36441  * Based on:
36442  * Ext JS Library 1.1.1
36443  * Copyright(c) 2006-2007, Ext JS, LLC.
36444  *
36445  * Originally Released Under LGPL - original licence link has changed is not relivant.
36446  *
36447  * Fork - LGPL
36448  * <script type="text/javascript">
36449  */
36450  
36451
36452
36453 /**
36454  * @class Roo.SplitLayoutRegion
36455  * @extends Roo.LayoutRegion
36456  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36457  */
36458 Roo.bootstrap.layout.Split = function(config){
36459     this.cursor = config.cursor;
36460     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36461 };
36462
36463 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36464 {
36465     splitTip : "Drag to resize.",
36466     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36467     useSplitTips : false,
36468
36469     applyConfig : function(config){
36470         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36471     },
36472     
36473     onRender : function(ctr,pos) {
36474         
36475         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36476         if(!this.config.split){
36477             return;
36478         }
36479         if(!this.split){
36480             
36481             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36482                             tag: "div",
36483                             id: this.el.id + "-split",
36484                             cls: "roo-layout-split roo-layout-split-"+this.position,
36485                             html: "&#160;"
36486             });
36487             /** The SplitBar for this region 
36488             * @type Roo.SplitBar */
36489             // does not exist yet...
36490             Roo.log([this.position, this.orientation]);
36491             
36492             this.split = new Roo.bootstrap.SplitBar({
36493                 dragElement : splitEl,
36494                 resizingElement: this.el,
36495                 orientation : this.orientation
36496             });
36497             
36498             this.split.on("moved", this.onSplitMove, this);
36499             this.split.useShim = this.config.useShim === true;
36500             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36501             if(this.useSplitTips){
36502                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36503             }
36504             //if(config.collapsible){
36505             //    this.split.el.on("dblclick", this.collapse,  this);
36506             //}
36507         }
36508         if(typeof this.config.minSize != "undefined"){
36509             this.split.minSize = this.config.minSize;
36510         }
36511         if(typeof this.config.maxSize != "undefined"){
36512             this.split.maxSize = this.config.maxSize;
36513         }
36514         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36515             this.hideSplitter();
36516         }
36517         
36518     },
36519
36520     getHMaxSize : function(){
36521          var cmax = this.config.maxSize || 10000;
36522          var center = this.mgr.getRegion("center");
36523          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36524     },
36525
36526     getVMaxSize : function(){
36527          var cmax = this.config.maxSize || 10000;
36528          var center = this.mgr.getRegion("center");
36529          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36530     },
36531
36532     onSplitMove : function(split, newSize){
36533         this.fireEvent("resized", this, newSize);
36534     },
36535     
36536     /** 
36537      * Returns the {@link Roo.SplitBar} for this region.
36538      * @return {Roo.SplitBar}
36539      */
36540     getSplitBar : function(){
36541         return this.split;
36542     },
36543     
36544     hide : function(){
36545         this.hideSplitter();
36546         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36547     },
36548
36549     hideSplitter : function(){
36550         if(this.split){
36551             this.split.el.setLocation(-2000,-2000);
36552             this.split.el.hide();
36553         }
36554     },
36555
36556     show : function(){
36557         if(this.split){
36558             this.split.el.show();
36559         }
36560         Roo.bootstrap.layout.Split.superclass.show.call(this);
36561     },
36562     
36563     beforeSlide: function(){
36564         if(Roo.isGecko){// firefox overflow auto bug workaround
36565             this.bodyEl.clip();
36566             if(this.tabs) {
36567                 this.tabs.bodyEl.clip();
36568             }
36569             if(this.activePanel){
36570                 this.activePanel.getEl().clip();
36571                 
36572                 if(this.activePanel.beforeSlide){
36573                     this.activePanel.beforeSlide();
36574                 }
36575             }
36576         }
36577     },
36578     
36579     afterSlide : function(){
36580         if(Roo.isGecko){// firefox overflow auto bug workaround
36581             this.bodyEl.unclip();
36582             if(this.tabs) {
36583                 this.tabs.bodyEl.unclip();
36584             }
36585             if(this.activePanel){
36586                 this.activePanel.getEl().unclip();
36587                 if(this.activePanel.afterSlide){
36588                     this.activePanel.afterSlide();
36589                 }
36590             }
36591         }
36592     },
36593
36594     initAutoHide : function(){
36595         if(this.autoHide !== false){
36596             if(!this.autoHideHd){
36597                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36598                 this.autoHideHd = {
36599                     "mouseout": function(e){
36600                         if(!e.within(this.el, true)){
36601                             st.delay(500);
36602                         }
36603                     },
36604                     "mouseover" : function(e){
36605                         st.cancel();
36606                     },
36607                     scope : this
36608                 };
36609             }
36610             this.el.on(this.autoHideHd);
36611         }
36612     },
36613
36614     clearAutoHide : function(){
36615         if(this.autoHide !== false){
36616             this.el.un("mouseout", this.autoHideHd.mouseout);
36617             this.el.un("mouseover", this.autoHideHd.mouseover);
36618         }
36619     },
36620
36621     clearMonitor : function(){
36622         Roo.get(document).un("click", this.slideInIf, this);
36623     },
36624
36625     // these names are backwards but not changed for compat
36626     slideOut : function(){
36627         if(this.isSlid || this.el.hasActiveFx()){
36628             return;
36629         }
36630         this.isSlid = true;
36631         if(this.collapseBtn){
36632             this.collapseBtn.hide();
36633         }
36634         this.closeBtnState = this.closeBtn.getStyle('display');
36635         this.closeBtn.hide();
36636         if(this.stickBtn){
36637             this.stickBtn.show();
36638         }
36639         this.el.show();
36640         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36641         this.beforeSlide();
36642         this.el.setStyle("z-index", 10001);
36643         this.el.slideIn(this.getSlideAnchor(), {
36644             callback: function(){
36645                 this.afterSlide();
36646                 this.initAutoHide();
36647                 Roo.get(document).on("click", this.slideInIf, this);
36648                 this.fireEvent("slideshow", this);
36649             },
36650             scope: this,
36651             block: true
36652         });
36653     },
36654
36655     afterSlideIn : function(){
36656         this.clearAutoHide();
36657         this.isSlid = false;
36658         this.clearMonitor();
36659         this.el.setStyle("z-index", "");
36660         if(this.collapseBtn){
36661             this.collapseBtn.show();
36662         }
36663         this.closeBtn.setStyle('display', this.closeBtnState);
36664         if(this.stickBtn){
36665             this.stickBtn.hide();
36666         }
36667         this.fireEvent("slidehide", this);
36668     },
36669
36670     slideIn : function(cb){
36671         if(!this.isSlid || this.el.hasActiveFx()){
36672             Roo.callback(cb);
36673             return;
36674         }
36675         this.isSlid = false;
36676         this.beforeSlide();
36677         this.el.slideOut(this.getSlideAnchor(), {
36678             callback: function(){
36679                 this.el.setLeftTop(-10000, -10000);
36680                 this.afterSlide();
36681                 this.afterSlideIn();
36682                 Roo.callback(cb);
36683             },
36684             scope: this,
36685             block: true
36686         });
36687     },
36688     
36689     slideInIf : function(e){
36690         if(!e.within(this.el)){
36691             this.slideIn();
36692         }
36693     },
36694
36695     animateCollapse : function(){
36696         this.beforeSlide();
36697         this.el.setStyle("z-index", 20000);
36698         var anchor = this.getSlideAnchor();
36699         this.el.slideOut(anchor, {
36700             callback : function(){
36701                 this.el.setStyle("z-index", "");
36702                 this.collapsedEl.slideIn(anchor, {duration:.3});
36703                 this.afterSlide();
36704                 this.el.setLocation(-10000,-10000);
36705                 this.el.hide();
36706                 this.fireEvent("collapsed", this);
36707             },
36708             scope: this,
36709             block: true
36710         });
36711     },
36712
36713     animateExpand : function(){
36714         this.beforeSlide();
36715         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36716         this.el.setStyle("z-index", 20000);
36717         this.collapsedEl.hide({
36718             duration:.1
36719         });
36720         this.el.slideIn(this.getSlideAnchor(), {
36721             callback : function(){
36722                 this.el.setStyle("z-index", "");
36723                 this.afterSlide();
36724                 if(this.split){
36725                     this.split.el.show();
36726                 }
36727                 this.fireEvent("invalidated", this);
36728                 this.fireEvent("expanded", this);
36729             },
36730             scope: this,
36731             block: true
36732         });
36733     },
36734
36735     anchors : {
36736         "west" : "left",
36737         "east" : "right",
36738         "north" : "top",
36739         "south" : "bottom"
36740     },
36741
36742     sanchors : {
36743         "west" : "l",
36744         "east" : "r",
36745         "north" : "t",
36746         "south" : "b"
36747     },
36748
36749     canchors : {
36750         "west" : "tl-tr",
36751         "east" : "tr-tl",
36752         "north" : "tl-bl",
36753         "south" : "bl-tl"
36754     },
36755
36756     getAnchor : function(){
36757         return this.anchors[this.position];
36758     },
36759
36760     getCollapseAnchor : function(){
36761         return this.canchors[this.position];
36762     },
36763
36764     getSlideAnchor : function(){
36765         return this.sanchors[this.position];
36766     },
36767
36768     getAlignAdj : function(){
36769         var cm = this.cmargins;
36770         switch(this.position){
36771             case "west":
36772                 return [0, 0];
36773             break;
36774             case "east":
36775                 return [0, 0];
36776             break;
36777             case "north":
36778                 return [0, 0];
36779             break;
36780             case "south":
36781                 return [0, 0];
36782             break;
36783         }
36784     },
36785
36786     getExpandAdj : function(){
36787         var c = this.collapsedEl, cm = this.cmargins;
36788         switch(this.position){
36789             case "west":
36790                 return [-(cm.right+c.getWidth()+cm.left), 0];
36791             break;
36792             case "east":
36793                 return [cm.right+c.getWidth()+cm.left, 0];
36794             break;
36795             case "north":
36796                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36797             break;
36798             case "south":
36799                 return [0, cm.top+cm.bottom+c.getHeight()];
36800             break;
36801         }
36802     }
36803 });/*
36804  * Based on:
36805  * Ext JS Library 1.1.1
36806  * Copyright(c) 2006-2007, Ext JS, LLC.
36807  *
36808  * Originally Released Under LGPL - original licence link has changed is not relivant.
36809  *
36810  * Fork - LGPL
36811  * <script type="text/javascript">
36812  */
36813 /*
36814  * These classes are private internal classes
36815  */
36816 Roo.bootstrap.layout.Center = function(config){
36817     config.region = "center";
36818     Roo.bootstrap.layout.Region.call(this, config);
36819     this.visible = true;
36820     this.minWidth = config.minWidth || 20;
36821     this.minHeight = config.minHeight || 20;
36822 };
36823
36824 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36825     hide : function(){
36826         // center panel can't be hidden
36827     },
36828     
36829     show : function(){
36830         // center panel can't be hidden
36831     },
36832     
36833     getMinWidth: function(){
36834         return this.minWidth;
36835     },
36836     
36837     getMinHeight: function(){
36838         return this.minHeight;
36839     }
36840 });
36841
36842
36843
36844
36845  
36846
36847
36848
36849
36850
36851 Roo.bootstrap.layout.North = function(config)
36852 {
36853     config.region = 'north';
36854     config.cursor = 'n-resize';
36855     
36856     Roo.bootstrap.layout.Split.call(this, config);
36857     
36858     
36859     if(this.split){
36860         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36861         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36862         this.split.el.addClass("roo-layout-split-v");
36863     }
36864     var size = config.initialSize || config.height;
36865     if(typeof size != "undefined"){
36866         this.el.setHeight(size);
36867     }
36868 };
36869 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36870 {
36871     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36872     
36873     
36874     
36875     getBox : function(){
36876         if(this.collapsed){
36877             return this.collapsedEl.getBox();
36878         }
36879         var box = this.el.getBox();
36880         if(this.split){
36881             box.height += this.split.el.getHeight();
36882         }
36883         return box;
36884     },
36885     
36886     updateBox : function(box){
36887         if(this.split && !this.collapsed){
36888             box.height -= this.split.el.getHeight();
36889             this.split.el.setLeft(box.x);
36890             this.split.el.setTop(box.y+box.height);
36891             this.split.el.setWidth(box.width);
36892         }
36893         if(this.collapsed){
36894             this.updateBody(box.width, null);
36895         }
36896         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36897     }
36898 });
36899
36900
36901
36902
36903
36904 Roo.bootstrap.layout.South = function(config){
36905     config.region = 'south';
36906     config.cursor = 's-resize';
36907     Roo.bootstrap.layout.Split.call(this, config);
36908     if(this.split){
36909         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36910         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36911         this.split.el.addClass("roo-layout-split-v");
36912     }
36913     var size = config.initialSize || config.height;
36914     if(typeof size != "undefined"){
36915         this.el.setHeight(size);
36916     }
36917 };
36918
36919 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36920     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36921     getBox : function(){
36922         if(this.collapsed){
36923             return this.collapsedEl.getBox();
36924         }
36925         var box = this.el.getBox();
36926         if(this.split){
36927             var sh = this.split.el.getHeight();
36928             box.height += sh;
36929             box.y -= sh;
36930         }
36931         return box;
36932     },
36933     
36934     updateBox : function(box){
36935         if(this.split && !this.collapsed){
36936             var sh = this.split.el.getHeight();
36937             box.height -= sh;
36938             box.y += sh;
36939             this.split.el.setLeft(box.x);
36940             this.split.el.setTop(box.y-sh);
36941             this.split.el.setWidth(box.width);
36942         }
36943         if(this.collapsed){
36944             this.updateBody(box.width, null);
36945         }
36946         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36947     }
36948 });
36949
36950 Roo.bootstrap.layout.East = function(config){
36951     config.region = "east";
36952     config.cursor = "e-resize";
36953     Roo.bootstrap.layout.Split.call(this, config);
36954     if(this.split){
36955         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36956         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36957         this.split.el.addClass("roo-layout-split-h");
36958     }
36959     var size = config.initialSize || config.width;
36960     if(typeof size != "undefined"){
36961         this.el.setWidth(size);
36962     }
36963 };
36964 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36965     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36966     getBox : function(){
36967         if(this.collapsed){
36968             return this.collapsedEl.getBox();
36969         }
36970         var box = this.el.getBox();
36971         if(this.split){
36972             var sw = this.split.el.getWidth();
36973             box.width += sw;
36974             box.x -= sw;
36975         }
36976         return box;
36977     },
36978
36979     updateBox : function(box){
36980         if(this.split && !this.collapsed){
36981             var sw = this.split.el.getWidth();
36982             box.width -= sw;
36983             this.split.el.setLeft(box.x);
36984             this.split.el.setTop(box.y);
36985             this.split.el.setHeight(box.height);
36986             box.x += sw;
36987         }
36988         if(this.collapsed){
36989             this.updateBody(null, box.height);
36990         }
36991         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36992     }
36993 });
36994
36995 Roo.bootstrap.layout.West = function(config){
36996     config.region = "west";
36997     config.cursor = "w-resize";
36998     
36999     Roo.bootstrap.layout.Split.call(this, config);
37000     if(this.split){
37001         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37002         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37003         this.split.el.addClass("roo-layout-split-h");
37004     }
37005     
37006 };
37007 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37008     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37009     
37010     onRender: function(ctr, pos)
37011     {
37012         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37013         var size = this.config.initialSize || this.config.width;
37014         if(typeof size != "undefined"){
37015             this.el.setWidth(size);
37016         }
37017     },
37018     
37019     getBox : function(){
37020         if(this.collapsed){
37021             return this.collapsedEl.getBox();
37022         }
37023         var box = this.el.getBox();
37024         if(this.split){
37025             box.width += this.split.el.getWidth();
37026         }
37027         return box;
37028     },
37029     
37030     updateBox : function(box){
37031         if(this.split && !this.collapsed){
37032             var sw = this.split.el.getWidth();
37033             box.width -= sw;
37034             this.split.el.setLeft(box.x+box.width);
37035             this.split.el.setTop(box.y);
37036             this.split.el.setHeight(box.height);
37037         }
37038         if(this.collapsed){
37039             this.updateBody(null, box.height);
37040         }
37041         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37042     }
37043 });
37044 Roo.namespace("Roo.bootstrap.panel");/*
37045  * Based on:
37046  * Ext JS Library 1.1.1
37047  * Copyright(c) 2006-2007, Ext JS, LLC.
37048  *
37049  * Originally Released Under LGPL - original licence link has changed is not relivant.
37050  *
37051  * Fork - LGPL
37052  * <script type="text/javascript">
37053  */
37054 /**
37055  * @class Roo.ContentPanel
37056  * @extends Roo.util.Observable
37057  * A basic ContentPanel element.
37058  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37059  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37060  * @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
37061  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37062  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37063  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37064  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37065  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37066  * @cfg {String} title          The title for this panel
37067  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37068  * @cfg {String} url            Calls {@link #setUrl} with this value
37069  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37070  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37071  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37072  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37073  * @cfg {Boolean} badges render the badges
37074
37075  * @constructor
37076  * Create a new ContentPanel.
37077  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37078  * @param {String/Object} config A string to set only the title or a config object
37079  * @param {String} content (optional) Set the HTML content for this panel
37080  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37081  */
37082 Roo.bootstrap.panel.Content = function( config){
37083     
37084     this.tpl = config.tpl || false;
37085     
37086     var el = config.el;
37087     var content = config.content;
37088
37089     if(config.autoCreate){ // xtype is available if this is called from factory
37090         el = Roo.id();
37091     }
37092     this.el = Roo.get(el);
37093     if(!this.el && config && config.autoCreate){
37094         if(typeof config.autoCreate == "object"){
37095             if(!config.autoCreate.id){
37096                 config.autoCreate.id = config.id||el;
37097             }
37098             this.el = Roo.DomHelper.append(document.body,
37099                         config.autoCreate, true);
37100         }else{
37101             var elcfg =  {   tag: "div",
37102                             cls: "roo-layout-inactive-content",
37103                             id: config.id||el
37104                             };
37105             if (config.html) {
37106                 elcfg.html = config.html;
37107                 
37108             }
37109                         
37110             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37111         }
37112     } 
37113     this.closable = false;
37114     this.loaded = false;
37115     this.active = false;
37116    
37117       
37118     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37119         
37120         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37121         
37122         this.wrapEl = this.el; //this.el.wrap();
37123         var ti = [];
37124         if (config.toolbar.items) {
37125             ti = config.toolbar.items ;
37126             delete config.toolbar.items ;
37127         }
37128         
37129         var nitems = [];
37130         this.toolbar.render(this.wrapEl, 'before');
37131         for(var i =0;i < ti.length;i++) {
37132           //  Roo.log(['add child', items[i]]);
37133             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37134         }
37135         this.toolbar.items = nitems;
37136         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37137         delete config.toolbar;
37138         
37139     }
37140     /*
37141     // xtype created footer. - not sure if will work as we normally have to render first..
37142     if (this.footer && !this.footer.el && this.footer.xtype) {
37143         if (!this.wrapEl) {
37144             this.wrapEl = this.el.wrap();
37145         }
37146     
37147         this.footer.container = this.wrapEl.createChild();
37148          
37149         this.footer = Roo.factory(this.footer, Roo);
37150         
37151     }
37152     */
37153     
37154      if(typeof config == "string"){
37155         this.title = config;
37156     }else{
37157         Roo.apply(this, config);
37158     }
37159     
37160     if(this.resizeEl){
37161         this.resizeEl = Roo.get(this.resizeEl, true);
37162     }else{
37163         this.resizeEl = this.el;
37164     }
37165     // handle view.xtype
37166     
37167  
37168     
37169     
37170     this.addEvents({
37171         /**
37172          * @event activate
37173          * Fires when this panel is activated. 
37174          * @param {Roo.ContentPanel} this
37175          */
37176         "activate" : true,
37177         /**
37178          * @event deactivate
37179          * Fires when this panel is activated. 
37180          * @param {Roo.ContentPanel} this
37181          */
37182         "deactivate" : true,
37183
37184         /**
37185          * @event resize
37186          * Fires when this panel is resized if fitToFrame is true.
37187          * @param {Roo.ContentPanel} this
37188          * @param {Number} width The width after any component adjustments
37189          * @param {Number} height The height after any component adjustments
37190          */
37191         "resize" : true,
37192         
37193          /**
37194          * @event render
37195          * Fires when this tab is created
37196          * @param {Roo.ContentPanel} this
37197          */
37198         "render" : true
37199         
37200         
37201         
37202     });
37203     
37204
37205     
37206     
37207     if(this.autoScroll){
37208         this.resizeEl.setStyle("overflow", "auto");
37209     } else {
37210         // fix randome scrolling
37211         //this.el.on('scroll', function() {
37212         //    Roo.log('fix random scolling');
37213         //    this.scrollTo('top',0); 
37214         //});
37215     }
37216     content = content || this.content;
37217     if(content){
37218         this.setContent(content);
37219     }
37220     if(config && config.url){
37221         this.setUrl(this.url, this.params, this.loadOnce);
37222     }
37223     
37224     
37225     
37226     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37227     
37228     if (this.view && typeof(this.view.xtype) != 'undefined') {
37229         this.view.el = this.el.appendChild(document.createElement("div"));
37230         this.view = Roo.factory(this.view); 
37231         this.view.render  &&  this.view.render(false, '');  
37232     }
37233     
37234     
37235     this.fireEvent('render', this);
37236 };
37237
37238 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37239     
37240     tabTip : '',
37241     
37242     setRegion : function(region){
37243         this.region = region;
37244         this.setActiveClass(region && !this.background);
37245     },
37246     
37247     
37248     setActiveClass: function(state)
37249     {
37250         if(state){
37251            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37252            this.el.setStyle('position','relative');
37253         }else{
37254            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37255            this.el.setStyle('position', 'absolute');
37256         } 
37257     },
37258     
37259     /**
37260      * Returns the toolbar for this Panel if one was configured. 
37261      * @return {Roo.Toolbar} 
37262      */
37263     getToolbar : function(){
37264         return this.toolbar;
37265     },
37266     
37267     setActiveState : function(active)
37268     {
37269         this.active = active;
37270         this.setActiveClass(active);
37271         if(!active){
37272             if(this.fireEvent("deactivate", this) === false){
37273                 return false;
37274             }
37275             return true;
37276         }
37277         this.fireEvent("activate", this);
37278         return true;
37279     },
37280     /**
37281      * Updates this panel's element
37282      * @param {String} content The new content
37283      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37284     */
37285     setContent : function(content, loadScripts){
37286         this.el.update(content, loadScripts);
37287     },
37288
37289     ignoreResize : function(w, h){
37290         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37291             return true;
37292         }else{
37293             this.lastSize = {width: w, height: h};
37294             return false;
37295         }
37296     },
37297     /**
37298      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37299      * @return {Roo.UpdateManager} The UpdateManager
37300      */
37301     getUpdateManager : function(){
37302         return this.el.getUpdateManager();
37303     },
37304      /**
37305      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37306      * @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:
37307 <pre><code>
37308 panel.load({
37309     url: "your-url.php",
37310     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37311     callback: yourFunction,
37312     scope: yourObject, //(optional scope)
37313     discardUrl: false,
37314     nocache: false,
37315     text: "Loading...",
37316     timeout: 30,
37317     scripts: false
37318 });
37319 </code></pre>
37320      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37321      * 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.
37322      * @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}
37323      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37324      * @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.
37325      * @return {Roo.ContentPanel} this
37326      */
37327     load : function(){
37328         var um = this.el.getUpdateManager();
37329         um.update.apply(um, arguments);
37330         return this;
37331     },
37332
37333
37334     /**
37335      * 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.
37336      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37337      * @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)
37338      * @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)
37339      * @return {Roo.UpdateManager} The UpdateManager
37340      */
37341     setUrl : function(url, params, loadOnce){
37342         if(this.refreshDelegate){
37343             this.removeListener("activate", this.refreshDelegate);
37344         }
37345         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37346         this.on("activate", this.refreshDelegate);
37347         return this.el.getUpdateManager();
37348     },
37349     
37350     _handleRefresh : function(url, params, loadOnce){
37351         if(!loadOnce || !this.loaded){
37352             var updater = this.el.getUpdateManager();
37353             updater.update(url, params, this._setLoaded.createDelegate(this));
37354         }
37355     },
37356     
37357     _setLoaded : function(){
37358         this.loaded = true;
37359     }, 
37360     
37361     /**
37362      * Returns this panel's id
37363      * @return {String} 
37364      */
37365     getId : function(){
37366         return this.el.id;
37367     },
37368     
37369     /** 
37370      * Returns this panel's element - used by regiosn to add.
37371      * @return {Roo.Element} 
37372      */
37373     getEl : function(){
37374         return this.wrapEl || this.el;
37375     },
37376     
37377    
37378     
37379     adjustForComponents : function(width, height)
37380     {
37381         //Roo.log('adjustForComponents ');
37382         if(this.resizeEl != this.el){
37383             width -= this.el.getFrameWidth('lr');
37384             height -= this.el.getFrameWidth('tb');
37385         }
37386         if(this.toolbar){
37387             var te = this.toolbar.getEl();
37388             te.setWidth(width);
37389             height -= te.getHeight();
37390         }
37391         if(this.footer){
37392             var te = this.footer.getEl();
37393             te.setWidth(width);
37394             height -= te.getHeight();
37395         }
37396         
37397         
37398         if(this.adjustments){
37399             width += this.adjustments[0];
37400             height += this.adjustments[1];
37401         }
37402         return {"width": width, "height": height};
37403     },
37404     
37405     setSize : function(width, height){
37406         if(this.fitToFrame && !this.ignoreResize(width, height)){
37407             if(this.fitContainer && this.resizeEl != this.el){
37408                 this.el.setSize(width, height);
37409             }
37410             var size = this.adjustForComponents(width, height);
37411             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37412             this.fireEvent('resize', this, size.width, size.height);
37413         }
37414     },
37415     
37416     /**
37417      * Returns this panel's title
37418      * @return {String} 
37419      */
37420     getTitle : function(){
37421         
37422         if (typeof(this.title) != 'object') {
37423             return this.title;
37424         }
37425         
37426         var t = '';
37427         for (var k in this.title) {
37428             if (!this.title.hasOwnProperty(k)) {
37429                 continue;
37430             }
37431             
37432             if (k.indexOf('-') >= 0) {
37433                 var s = k.split('-');
37434                 for (var i = 0; i<s.length; i++) {
37435                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37436                 }
37437             } else {
37438                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37439             }
37440         }
37441         return t;
37442     },
37443     
37444     /**
37445      * Set this panel's title
37446      * @param {String} title
37447      */
37448     setTitle : function(title){
37449         this.title = title;
37450         if(this.region){
37451             this.region.updatePanelTitle(this, title);
37452         }
37453     },
37454     
37455     /**
37456      * Returns true is this panel was configured to be closable
37457      * @return {Boolean} 
37458      */
37459     isClosable : function(){
37460         return this.closable;
37461     },
37462     
37463     beforeSlide : function(){
37464         this.el.clip();
37465         this.resizeEl.clip();
37466     },
37467     
37468     afterSlide : function(){
37469         this.el.unclip();
37470         this.resizeEl.unclip();
37471     },
37472     
37473     /**
37474      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37475      *   Will fail silently if the {@link #setUrl} method has not been called.
37476      *   This does not activate the panel, just updates its content.
37477      */
37478     refresh : function(){
37479         if(this.refreshDelegate){
37480            this.loaded = false;
37481            this.refreshDelegate();
37482         }
37483     },
37484     
37485     /**
37486      * Destroys this panel
37487      */
37488     destroy : function(){
37489         this.el.removeAllListeners();
37490         var tempEl = document.createElement("span");
37491         tempEl.appendChild(this.el.dom);
37492         tempEl.innerHTML = "";
37493         this.el.remove();
37494         this.el = null;
37495     },
37496     
37497     /**
37498      * form - if the content panel contains a form - this is a reference to it.
37499      * @type {Roo.form.Form}
37500      */
37501     form : false,
37502     /**
37503      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37504      *    This contains a reference to it.
37505      * @type {Roo.View}
37506      */
37507     view : false,
37508     
37509       /**
37510      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37511      * <pre><code>
37512
37513 layout.addxtype({
37514        xtype : 'Form',
37515        items: [ .... ]
37516    }
37517 );
37518
37519 </code></pre>
37520      * @param {Object} cfg Xtype definition of item to add.
37521      */
37522     
37523     
37524     getChildContainer: function () {
37525         return this.getEl();
37526     }
37527     
37528     
37529     /*
37530         var  ret = new Roo.factory(cfg);
37531         return ret;
37532         
37533         
37534         // add form..
37535         if (cfg.xtype.match(/^Form$/)) {
37536             
37537             var el;
37538             //if (this.footer) {
37539             //    el = this.footer.container.insertSibling(false, 'before');
37540             //} else {
37541                 el = this.el.createChild();
37542             //}
37543
37544             this.form = new  Roo.form.Form(cfg);
37545             
37546             
37547             if ( this.form.allItems.length) {
37548                 this.form.render(el.dom);
37549             }
37550             return this.form;
37551         }
37552         // should only have one of theses..
37553         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37554             // views.. should not be just added - used named prop 'view''
37555             
37556             cfg.el = this.el.appendChild(document.createElement("div"));
37557             // factory?
37558             
37559             var ret = new Roo.factory(cfg);
37560              
37561              ret.render && ret.render(false, ''); // render blank..
37562             this.view = ret;
37563             return ret;
37564         }
37565         return false;
37566     }
37567     \*/
37568 });
37569  
37570 /**
37571  * @class Roo.bootstrap.panel.Grid
37572  * @extends Roo.bootstrap.panel.Content
37573  * @constructor
37574  * Create a new GridPanel.
37575  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37576  * @param {Object} config A the config object
37577   
37578  */
37579
37580
37581
37582 Roo.bootstrap.panel.Grid = function(config)
37583 {
37584     
37585       
37586     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37587         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37588
37589     config.el = this.wrapper;
37590     //this.el = this.wrapper;
37591     
37592       if (config.container) {
37593         // ctor'ed from a Border/panel.grid
37594         
37595         
37596         this.wrapper.setStyle("overflow", "hidden");
37597         this.wrapper.addClass('roo-grid-container');
37598
37599     }
37600     
37601     
37602     if(config.toolbar){
37603         var tool_el = this.wrapper.createChild();    
37604         this.toolbar = Roo.factory(config.toolbar);
37605         var ti = [];
37606         if (config.toolbar.items) {
37607             ti = config.toolbar.items ;
37608             delete config.toolbar.items ;
37609         }
37610         
37611         var nitems = [];
37612         this.toolbar.render(tool_el);
37613         for(var i =0;i < ti.length;i++) {
37614           //  Roo.log(['add child', items[i]]);
37615             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37616         }
37617         this.toolbar.items = nitems;
37618         
37619         delete config.toolbar;
37620     }
37621     
37622     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37623     config.grid.scrollBody = true;;
37624     config.grid.monitorWindowResize = false; // turn off autosizing
37625     config.grid.autoHeight = false;
37626     config.grid.autoWidth = false;
37627     
37628     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37629     
37630     if (config.background) {
37631         // render grid on panel activation (if panel background)
37632         this.on('activate', function(gp) {
37633             if (!gp.grid.rendered) {
37634                 gp.grid.render(this.wrapper);
37635                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37636             }
37637         });
37638             
37639     } else {
37640         this.grid.render(this.wrapper);
37641         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37642
37643     }
37644     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37645     // ??? needed ??? config.el = this.wrapper;
37646     
37647     
37648     
37649   
37650     // xtype created footer. - not sure if will work as we normally have to render first..
37651     if (this.footer && !this.footer.el && this.footer.xtype) {
37652         
37653         var ctr = this.grid.getView().getFooterPanel(true);
37654         this.footer.dataSource = this.grid.dataSource;
37655         this.footer = Roo.factory(this.footer, Roo);
37656         this.footer.render(ctr);
37657         
37658     }
37659     
37660     
37661     
37662     
37663      
37664 };
37665
37666 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37667     getId : function(){
37668         return this.grid.id;
37669     },
37670     
37671     /**
37672      * Returns the grid for this panel
37673      * @return {Roo.bootstrap.Table} 
37674      */
37675     getGrid : function(){
37676         return this.grid;    
37677     },
37678     
37679     setSize : function(width, height){
37680         if(!this.ignoreResize(width, height)){
37681             var grid = this.grid;
37682             var size = this.adjustForComponents(width, height);
37683             var gridel = grid.getGridEl();
37684             gridel.setSize(size.width, size.height);
37685             /*
37686             var thd = grid.getGridEl().select('thead',true).first();
37687             var tbd = grid.getGridEl().select('tbody', true).first();
37688             if (tbd) {
37689                 tbd.setSize(width, height - thd.getHeight());
37690             }
37691             */
37692             grid.autoSize();
37693         }
37694     },
37695      
37696     
37697     
37698     beforeSlide : function(){
37699         this.grid.getView().scroller.clip();
37700     },
37701     
37702     afterSlide : function(){
37703         this.grid.getView().scroller.unclip();
37704     },
37705     
37706     destroy : function(){
37707         this.grid.destroy();
37708         delete this.grid;
37709         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37710     }
37711 });
37712
37713 /**
37714  * @class Roo.bootstrap.panel.Nest
37715  * @extends Roo.bootstrap.panel.Content
37716  * @constructor
37717  * Create a new Panel, that can contain a layout.Border.
37718  * 
37719  * 
37720  * @param {Roo.BorderLayout} layout The layout for this panel
37721  * @param {String/Object} config A string to set only the title or a config object
37722  */
37723 Roo.bootstrap.panel.Nest = function(config)
37724 {
37725     // construct with only one argument..
37726     /* FIXME - implement nicer consturctors
37727     if (layout.layout) {
37728         config = layout;
37729         layout = config.layout;
37730         delete config.layout;
37731     }
37732     if (layout.xtype && !layout.getEl) {
37733         // then layout needs constructing..
37734         layout = Roo.factory(layout, Roo);
37735     }
37736     */
37737     
37738     config.el =  config.layout.getEl();
37739     
37740     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37741     
37742     config.layout.monitorWindowResize = false; // turn off autosizing
37743     this.layout = config.layout;
37744     this.layout.getEl().addClass("roo-layout-nested-layout");
37745     
37746     
37747     
37748     
37749 };
37750
37751 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37752
37753     setSize : function(width, height){
37754         if(!this.ignoreResize(width, height)){
37755             var size = this.adjustForComponents(width, height);
37756             var el = this.layout.getEl();
37757             if (size.height < 1) {
37758                 el.setWidth(size.width);   
37759             } else {
37760                 el.setSize(size.width, size.height);
37761             }
37762             var touch = el.dom.offsetWidth;
37763             this.layout.layout();
37764             // ie requires a double layout on the first pass
37765             if(Roo.isIE && !this.initialized){
37766                 this.initialized = true;
37767                 this.layout.layout();
37768             }
37769         }
37770     },
37771     
37772     // activate all subpanels if not currently active..
37773     
37774     setActiveState : function(active){
37775         this.active = active;
37776         this.setActiveClass(active);
37777         
37778         if(!active){
37779             this.fireEvent("deactivate", this);
37780             return;
37781         }
37782         
37783         this.fireEvent("activate", this);
37784         // not sure if this should happen before or after..
37785         if (!this.layout) {
37786             return; // should not happen..
37787         }
37788         var reg = false;
37789         for (var r in this.layout.regions) {
37790             reg = this.layout.getRegion(r);
37791             if (reg.getActivePanel()) {
37792                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37793                 reg.setActivePanel(reg.getActivePanel());
37794                 continue;
37795             }
37796             if (!reg.panels.length) {
37797                 continue;
37798             }
37799             reg.showPanel(reg.getPanel(0));
37800         }
37801         
37802         
37803         
37804         
37805     },
37806     
37807     /**
37808      * Returns the nested BorderLayout for this panel
37809      * @return {Roo.BorderLayout} 
37810      */
37811     getLayout : function(){
37812         return this.layout;
37813     },
37814     
37815      /**
37816      * Adds a xtype elements to the layout of the nested panel
37817      * <pre><code>
37818
37819 panel.addxtype({
37820        xtype : 'ContentPanel',
37821        region: 'west',
37822        items: [ .... ]
37823    }
37824 );
37825
37826 panel.addxtype({
37827         xtype : 'NestedLayoutPanel',
37828         region: 'west',
37829         layout: {
37830            center: { },
37831            west: { }   
37832         },
37833         items : [ ... list of content panels or nested layout panels.. ]
37834    }
37835 );
37836 </code></pre>
37837      * @param {Object} cfg Xtype definition of item to add.
37838      */
37839     addxtype : function(cfg) {
37840         return this.layout.addxtype(cfg);
37841     
37842     }
37843 });        /*
37844  * Based on:
37845  * Ext JS Library 1.1.1
37846  * Copyright(c) 2006-2007, Ext JS, LLC.
37847  *
37848  * Originally Released Under LGPL - original licence link has changed is not relivant.
37849  *
37850  * Fork - LGPL
37851  * <script type="text/javascript">
37852  */
37853 /**
37854  * @class Roo.TabPanel
37855  * @extends Roo.util.Observable
37856  * A lightweight tab container.
37857  * <br><br>
37858  * Usage:
37859  * <pre><code>
37860 // basic tabs 1, built from existing content
37861 var tabs = new Roo.TabPanel("tabs1");
37862 tabs.addTab("script", "View Script");
37863 tabs.addTab("markup", "View Markup");
37864 tabs.activate("script");
37865
37866 // more advanced tabs, built from javascript
37867 var jtabs = new Roo.TabPanel("jtabs");
37868 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37869
37870 // set up the UpdateManager
37871 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37872 var updater = tab2.getUpdateManager();
37873 updater.setDefaultUrl("ajax1.htm");
37874 tab2.on('activate', updater.refresh, updater, true);
37875
37876 // Use setUrl for Ajax loading
37877 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37878 tab3.setUrl("ajax2.htm", null, true);
37879
37880 // Disabled tab
37881 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37882 tab4.disable();
37883
37884 jtabs.activate("jtabs-1");
37885  * </code></pre>
37886  * @constructor
37887  * Create a new TabPanel.
37888  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37889  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37890  */
37891 Roo.bootstrap.panel.Tabs = function(config){
37892     /**
37893     * The container element for this TabPanel.
37894     * @type Roo.Element
37895     */
37896     this.el = Roo.get(config.el);
37897     delete config.el;
37898     if(config){
37899         if(typeof config == "boolean"){
37900             this.tabPosition = config ? "bottom" : "top";
37901         }else{
37902             Roo.apply(this, config);
37903         }
37904     }
37905     
37906     if(this.tabPosition == "bottom"){
37907         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37908         this.el.addClass("roo-tabs-bottom");
37909     }
37910     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37911     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37912     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37913     if(Roo.isIE){
37914         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37915     }
37916     if(this.tabPosition != "bottom"){
37917         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37918          * @type Roo.Element
37919          */
37920         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37921         this.el.addClass("roo-tabs-top");
37922     }
37923     this.items = [];
37924
37925     this.bodyEl.setStyle("position", "relative");
37926
37927     this.active = null;
37928     this.activateDelegate = this.activate.createDelegate(this);
37929
37930     this.addEvents({
37931         /**
37932          * @event tabchange
37933          * Fires when the active tab changes
37934          * @param {Roo.TabPanel} this
37935          * @param {Roo.TabPanelItem} activePanel The new active tab
37936          */
37937         "tabchange": true,
37938         /**
37939          * @event beforetabchange
37940          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37941          * @param {Roo.TabPanel} this
37942          * @param {Object} e Set cancel to true on this object to cancel the tab change
37943          * @param {Roo.TabPanelItem} tab The tab being changed to
37944          */
37945         "beforetabchange" : true
37946     });
37947
37948     Roo.EventManager.onWindowResize(this.onResize, this);
37949     this.cpad = this.el.getPadding("lr");
37950     this.hiddenCount = 0;
37951
37952
37953     // toolbar on the tabbar support...
37954     if (this.toolbar) {
37955         alert("no toolbar support yet");
37956         this.toolbar  = false;
37957         /*
37958         var tcfg = this.toolbar;
37959         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37960         this.toolbar = new Roo.Toolbar(tcfg);
37961         if (Roo.isSafari) {
37962             var tbl = tcfg.container.child('table', true);
37963             tbl.setAttribute('width', '100%');
37964         }
37965         */
37966         
37967     }
37968    
37969
37970
37971     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37972 };
37973
37974 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37975     /*
37976      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37977      */
37978     tabPosition : "top",
37979     /*
37980      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37981      */
37982     currentTabWidth : 0,
37983     /*
37984      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37985      */
37986     minTabWidth : 40,
37987     /*
37988      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37989      */
37990     maxTabWidth : 250,
37991     /*
37992      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37993      */
37994     preferredTabWidth : 175,
37995     /*
37996      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37997      */
37998     resizeTabs : false,
37999     /*
38000      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38001      */
38002     monitorResize : true,
38003     /*
38004      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38005      */
38006     toolbar : false,
38007
38008     /**
38009      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38010      * @param {String} id The id of the div to use <b>or create</b>
38011      * @param {String} text The text for the tab
38012      * @param {String} content (optional) Content to put in the TabPanelItem body
38013      * @param {Boolean} closable (optional) True to create a close icon on the tab
38014      * @return {Roo.TabPanelItem} The created TabPanelItem
38015      */
38016     addTab : function(id, text, content, closable, tpl)
38017     {
38018         var item = new Roo.bootstrap.panel.TabItem({
38019             panel: this,
38020             id : id,
38021             text : text,
38022             closable : closable,
38023             tpl : tpl
38024         });
38025         this.addTabItem(item);
38026         if(content){
38027             item.setContent(content);
38028         }
38029         return item;
38030     },
38031
38032     /**
38033      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38034      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38035      * @return {Roo.TabPanelItem}
38036      */
38037     getTab : function(id){
38038         return this.items[id];
38039     },
38040
38041     /**
38042      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38043      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38044      */
38045     hideTab : function(id){
38046         var t = this.items[id];
38047         if(!t.isHidden()){
38048            t.setHidden(true);
38049            this.hiddenCount++;
38050            this.autoSizeTabs();
38051         }
38052     },
38053
38054     /**
38055      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38056      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38057      */
38058     unhideTab : function(id){
38059         var t = this.items[id];
38060         if(t.isHidden()){
38061            t.setHidden(false);
38062            this.hiddenCount--;
38063            this.autoSizeTabs();
38064         }
38065     },
38066
38067     /**
38068      * Adds an existing {@link Roo.TabPanelItem}.
38069      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38070      */
38071     addTabItem : function(item){
38072         this.items[item.id] = item;
38073         this.items.push(item);
38074       //  if(this.resizeTabs){
38075     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38076   //         this.autoSizeTabs();
38077 //        }else{
38078 //            item.autoSize();
38079        // }
38080     },
38081
38082     /**
38083      * Removes a {@link Roo.TabPanelItem}.
38084      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38085      */
38086     removeTab : function(id){
38087         var items = this.items;
38088         var tab = items[id];
38089         if(!tab) { return; }
38090         var index = items.indexOf(tab);
38091         if(this.active == tab && items.length > 1){
38092             var newTab = this.getNextAvailable(index);
38093             if(newTab) {
38094                 newTab.activate();
38095             }
38096         }
38097         this.stripEl.dom.removeChild(tab.pnode.dom);
38098         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38099             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38100         }
38101         items.splice(index, 1);
38102         delete this.items[tab.id];
38103         tab.fireEvent("close", tab);
38104         tab.purgeListeners();
38105         this.autoSizeTabs();
38106     },
38107
38108     getNextAvailable : function(start){
38109         var items = this.items;
38110         var index = start;
38111         // look for a next tab that will slide over to
38112         // replace the one being removed
38113         while(index < items.length){
38114             var item = items[++index];
38115             if(item && !item.isHidden()){
38116                 return item;
38117             }
38118         }
38119         // if one isn't found select the previous tab (on the left)
38120         index = start;
38121         while(index >= 0){
38122             var item = items[--index];
38123             if(item && !item.isHidden()){
38124                 return item;
38125             }
38126         }
38127         return null;
38128     },
38129
38130     /**
38131      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38132      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38133      */
38134     disableTab : function(id){
38135         var tab = this.items[id];
38136         if(tab && this.active != tab){
38137             tab.disable();
38138         }
38139     },
38140
38141     /**
38142      * Enables a {@link Roo.TabPanelItem} that is disabled.
38143      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38144      */
38145     enableTab : function(id){
38146         var tab = this.items[id];
38147         tab.enable();
38148     },
38149
38150     /**
38151      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38152      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38153      * @return {Roo.TabPanelItem} The TabPanelItem.
38154      */
38155     activate : function(id){
38156         var tab = this.items[id];
38157         if(!tab){
38158             return null;
38159         }
38160         if(tab == this.active || tab.disabled){
38161             return tab;
38162         }
38163         var e = {};
38164         this.fireEvent("beforetabchange", this, e, tab);
38165         if(e.cancel !== true && !tab.disabled){
38166             if(this.active){
38167                 this.active.hide();
38168             }
38169             this.active = this.items[id];
38170             this.active.show();
38171             this.fireEvent("tabchange", this, this.active);
38172         }
38173         return tab;
38174     },
38175
38176     /**
38177      * Gets the active {@link Roo.TabPanelItem}.
38178      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38179      */
38180     getActiveTab : function(){
38181         return this.active;
38182     },
38183
38184     /**
38185      * Updates the tab body element to fit the height of the container element
38186      * for overflow scrolling
38187      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38188      */
38189     syncHeight : function(targetHeight){
38190         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38191         var bm = this.bodyEl.getMargins();
38192         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38193         this.bodyEl.setHeight(newHeight);
38194         return newHeight;
38195     },
38196
38197     onResize : function(){
38198         if(this.monitorResize){
38199             this.autoSizeTabs();
38200         }
38201     },
38202
38203     /**
38204      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38205      */
38206     beginUpdate : function(){
38207         this.updating = true;
38208     },
38209
38210     /**
38211      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38212      */
38213     endUpdate : function(){
38214         this.updating = false;
38215         this.autoSizeTabs();
38216     },
38217
38218     /**
38219      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38220      */
38221     autoSizeTabs : function(){
38222         var count = this.items.length;
38223         var vcount = count - this.hiddenCount;
38224         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38225             return;
38226         }
38227         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38228         var availWidth = Math.floor(w / vcount);
38229         var b = this.stripBody;
38230         if(b.getWidth() > w){
38231             var tabs = this.items;
38232             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38233             if(availWidth < this.minTabWidth){
38234                 /*if(!this.sleft){    // incomplete scrolling code
38235                     this.createScrollButtons();
38236                 }
38237                 this.showScroll();
38238                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38239             }
38240         }else{
38241             if(this.currentTabWidth < this.preferredTabWidth){
38242                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38243             }
38244         }
38245     },
38246
38247     /**
38248      * Returns the number of tabs in this TabPanel.
38249      * @return {Number}
38250      */
38251      getCount : function(){
38252          return this.items.length;
38253      },
38254
38255     /**
38256      * Resizes all the tabs to the passed width
38257      * @param {Number} The new width
38258      */
38259     setTabWidth : function(width){
38260         this.currentTabWidth = width;
38261         for(var i = 0, len = this.items.length; i < len; i++) {
38262                 if(!this.items[i].isHidden()) {
38263                 this.items[i].setWidth(width);
38264             }
38265         }
38266     },
38267
38268     /**
38269      * Destroys this TabPanel
38270      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38271      */
38272     destroy : function(removeEl){
38273         Roo.EventManager.removeResizeListener(this.onResize, this);
38274         for(var i = 0, len = this.items.length; i < len; i++){
38275             this.items[i].purgeListeners();
38276         }
38277         if(removeEl === true){
38278             this.el.update("");
38279             this.el.remove();
38280         }
38281     },
38282     
38283     createStrip : function(container)
38284     {
38285         var strip = document.createElement("nav");
38286         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38287         container.appendChild(strip);
38288         return strip;
38289     },
38290     
38291     createStripList : function(strip)
38292     {
38293         // div wrapper for retard IE
38294         // returns the "tr" element.
38295         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38296         //'<div class="x-tabs-strip-wrap">'+
38297           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38298           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38299         return strip.firstChild; //.firstChild.firstChild.firstChild;
38300     },
38301     createBody : function(container)
38302     {
38303         var body = document.createElement("div");
38304         Roo.id(body, "tab-body");
38305         //Roo.fly(body).addClass("x-tabs-body");
38306         Roo.fly(body).addClass("tab-content");
38307         container.appendChild(body);
38308         return body;
38309     },
38310     createItemBody :function(bodyEl, id){
38311         var body = Roo.getDom(id);
38312         if(!body){
38313             body = document.createElement("div");
38314             body.id = id;
38315         }
38316         //Roo.fly(body).addClass("x-tabs-item-body");
38317         Roo.fly(body).addClass("tab-pane");
38318          bodyEl.insertBefore(body, bodyEl.firstChild);
38319         return body;
38320     },
38321     /** @private */
38322     createStripElements :  function(stripEl, text, closable, tpl)
38323     {
38324         var td = document.createElement("li"); // was td..
38325         
38326         
38327         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38328         
38329         
38330         stripEl.appendChild(td);
38331         /*if(closable){
38332             td.className = "x-tabs-closable";
38333             if(!this.closeTpl){
38334                 this.closeTpl = new Roo.Template(
38335                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38336                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38337                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38338                 );
38339             }
38340             var el = this.closeTpl.overwrite(td, {"text": text});
38341             var close = el.getElementsByTagName("div")[0];
38342             var inner = el.getElementsByTagName("em")[0];
38343             return {"el": el, "close": close, "inner": inner};
38344         } else {
38345         */
38346         // not sure what this is..
38347 //            if(!this.tabTpl){
38348                 //this.tabTpl = new Roo.Template(
38349                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38350                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38351                 //);
38352 //                this.tabTpl = new Roo.Template(
38353 //                   '<a href="#">' +
38354 //                   '<span unselectable="on"' +
38355 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38356 //                            ' >{text}</span></a>'
38357 //                );
38358 //                
38359 //            }
38360
38361
38362             var template = tpl || this.tabTpl || false;
38363             
38364             if(!template){
38365                 
38366                 template = new Roo.Template(
38367                    '<a href="#">' +
38368                    '<span unselectable="on"' +
38369                             (this.disableTooltips ? '' : ' title="{text}"') +
38370                             ' >{text}</span></a>'
38371                 );
38372             }
38373             
38374             switch (typeof(template)) {
38375                 case 'object' :
38376                     break;
38377                 case 'string' :
38378                     template = new Roo.Template(template);
38379                     break;
38380                 default :
38381                     break;
38382             }
38383             
38384             var el = template.overwrite(td, {"text": text});
38385             
38386             var inner = el.getElementsByTagName("span")[0];
38387             
38388             return {"el": el, "inner": inner};
38389             
38390     }
38391         
38392     
38393 });
38394
38395 /**
38396  * @class Roo.TabPanelItem
38397  * @extends Roo.util.Observable
38398  * Represents an individual item (tab plus body) in a TabPanel.
38399  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38400  * @param {String} id The id of this TabPanelItem
38401  * @param {String} text The text for the tab of this TabPanelItem
38402  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38403  */
38404 Roo.bootstrap.panel.TabItem = function(config){
38405     /**
38406      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38407      * @type Roo.TabPanel
38408      */
38409     this.tabPanel = config.panel;
38410     /**
38411      * The id for this TabPanelItem
38412      * @type String
38413      */
38414     this.id = config.id;
38415     /** @private */
38416     this.disabled = false;
38417     /** @private */
38418     this.text = config.text;
38419     /** @private */
38420     this.loaded = false;
38421     this.closable = config.closable;
38422
38423     /**
38424      * The body element for this TabPanelItem.
38425      * @type Roo.Element
38426      */
38427     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38428     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38429     this.bodyEl.setStyle("display", "block");
38430     this.bodyEl.setStyle("zoom", "1");
38431     //this.hideAction();
38432
38433     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38434     /** @private */
38435     this.el = Roo.get(els.el);
38436     this.inner = Roo.get(els.inner, true);
38437     this.textEl = Roo.get(this.el.dom.firstChild, true);
38438     this.pnode = Roo.get(els.el.parentNode, true);
38439 //    this.el.on("mousedown", this.onTabMouseDown, this);
38440     this.el.on("click", this.onTabClick, this);
38441     /** @private */
38442     if(config.closable){
38443         var c = Roo.get(els.close, true);
38444         c.dom.title = this.closeText;
38445         c.addClassOnOver("close-over");
38446         c.on("click", this.closeClick, this);
38447      }
38448
38449     this.addEvents({
38450          /**
38451          * @event activate
38452          * Fires when this tab becomes the active tab.
38453          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38454          * @param {Roo.TabPanelItem} this
38455          */
38456         "activate": true,
38457         /**
38458          * @event beforeclose
38459          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38460          * @param {Roo.TabPanelItem} this
38461          * @param {Object} e Set cancel to true on this object to cancel the close.
38462          */
38463         "beforeclose": true,
38464         /**
38465          * @event close
38466          * Fires when this tab is closed.
38467          * @param {Roo.TabPanelItem} this
38468          */
38469          "close": true,
38470         /**
38471          * @event deactivate
38472          * Fires when this tab is no longer the active tab.
38473          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38474          * @param {Roo.TabPanelItem} this
38475          */
38476          "deactivate" : true
38477     });
38478     this.hidden = false;
38479
38480     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38481 };
38482
38483 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38484            {
38485     purgeListeners : function(){
38486        Roo.util.Observable.prototype.purgeListeners.call(this);
38487        this.el.removeAllListeners();
38488     },
38489     /**
38490      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38491      */
38492     show : function(){
38493         this.pnode.addClass("active");
38494         this.showAction();
38495         if(Roo.isOpera){
38496             this.tabPanel.stripWrap.repaint();
38497         }
38498         this.fireEvent("activate", this.tabPanel, this);
38499     },
38500
38501     /**
38502      * Returns true if this tab is the active tab.
38503      * @return {Boolean}
38504      */
38505     isActive : function(){
38506         return this.tabPanel.getActiveTab() == this;
38507     },
38508
38509     /**
38510      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38511      */
38512     hide : function(){
38513         this.pnode.removeClass("active");
38514         this.hideAction();
38515         this.fireEvent("deactivate", this.tabPanel, this);
38516     },
38517
38518     hideAction : function(){
38519         this.bodyEl.hide();
38520         this.bodyEl.setStyle("position", "absolute");
38521         this.bodyEl.setLeft("-20000px");
38522         this.bodyEl.setTop("-20000px");
38523     },
38524
38525     showAction : function(){
38526         this.bodyEl.setStyle("position", "relative");
38527         this.bodyEl.setTop("");
38528         this.bodyEl.setLeft("");
38529         this.bodyEl.show();
38530     },
38531
38532     /**
38533      * Set the tooltip for the tab.
38534      * @param {String} tooltip The tab's tooltip
38535      */
38536     setTooltip : function(text){
38537         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38538             this.textEl.dom.qtip = text;
38539             this.textEl.dom.removeAttribute('title');
38540         }else{
38541             this.textEl.dom.title = text;
38542         }
38543     },
38544
38545     onTabClick : function(e){
38546         e.preventDefault();
38547         this.tabPanel.activate(this.id);
38548     },
38549
38550     onTabMouseDown : function(e){
38551         e.preventDefault();
38552         this.tabPanel.activate(this.id);
38553     },
38554 /*
38555     getWidth : function(){
38556         return this.inner.getWidth();
38557     },
38558
38559     setWidth : function(width){
38560         var iwidth = width - this.pnode.getPadding("lr");
38561         this.inner.setWidth(iwidth);
38562         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38563         this.pnode.setWidth(width);
38564     },
38565 */
38566     /**
38567      * Show or hide the tab
38568      * @param {Boolean} hidden True to hide or false to show.
38569      */
38570     setHidden : function(hidden){
38571         this.hidden = hidden;
38572         this.pnode.setStyle("display", hidden ? "none" : "");
38573     },
38574
38575     /**
38576      * Returns true if this tab is "hidden"
38577      * @return {Boolean}
38578      */
38579     isHidden : function(){
38580         return this.hidden;
38581     },
38582
38583     /**
38584      * Returns the text for this tab
38585      * @return {String}
38586      */
38587     getText : function(){
38588         return this.text;
38589     },
38590     /*
38591     autoSize : function(){
38592         //this.el.beginMeasure();
38593         this.textEl.setWidth(1);
38594         /*
38595          *  #2804 [new] Tabs in Roojs
38596          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38597          */
38598         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38599         //this.el.endMeasure();
38600     //},
38601
38602     /**
38603      * Sets the text for the tab (Note: this also sets the tooltip text)
38604      * @param {String} text The tab's text and tooltip
38605      */
38606     setText : function(text){
38607         this.text = text;
38608         this.textEl.update(text);
38609         this.setTooltip(text);
38610         //if(!this.tabPanel.resizeTabs){
38611         //    this.autoSize();
38612         //}
38613     },
38614     /**
38615      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38616      */
38617     activate : function(){
38618         this.tabPanel.activate(this.id);
38619     },
38620
38621     /**
38622      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38623      */
38624     disable : function(){
38625         if(this.tabPanel.active != this){
38626             this.disabled = true;
38627             this.pnode.addClass("disabled");
38628         }
38629     },
38630
38631     /**
38632      * Enables this TabPanelItem if it was previously disabled.
38633      */
38634     enable : function(){
38635         this.disabled = false;
38636         this.pnode.removeClass("disabled");
38637     },
38638
38639     /**
38640      * Sets the content for this TabPanelItem.
38641      * @param {String} content The content
38642      * @param {Boolean} loadScripts true to look for and load scripts
38643      */
38644     setContent : function(content, loadScripts){
38645         this.bodyEl.update(content, loadScripts);
38646     },
38647
38648     /**
38649      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38650      * @return {Roo.UpdateManager} The UpdateManager
38651      */
38652     getUpdateManager : function(){
38653         return this.bodyEl.getUpdateManager();
38654     },
38655
38656     /**
38657      * Set a URL to be used to load the content for this TabPanelItem.
38658      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38659      * @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)
38660      * @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)
38661      * @return {Roo.UpdateManager} The UpdateManager
38662      */
38663     setUrl : function(url, params, loadOnce){
38664         if(this.refreshDelegate){
38665             this.un('activate', this.refreshDelegate);
38666         }
38667         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38668         this.on("activate", this.refreshDelegate);
38669         return this.bodyEl.getUpdateManager();
38670     },
38671
38672     /** @private */
38673     _handleRefresh : function(url, params, loadOnce){
38674         if(!loadOnce || !this.loaded){
38675             var updater = this.bodyEl.getUpdateManager();
38676             updater.update(url, params, this._setLoaded.createDelegate(this));
38677         }
38678     },
38679
38680     /**
38681      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38682      *   Will fail silently if the setUrl method has not been called.
38683      *   This does not activate the panel, just updates its content.
38684      */
38685     refresh : function(){
38686         if(this.refreshDelegate){
38687            this.loaded = false;
38688            this.refreshDelegate();
38689         }
38690     },
38691
38692     /** @private */
38693     _setLoaded : function(){
38694         this.loaded = true;
38695     },
38696
38697     /** @private */
38698     closeClick : function(e){
38699         var o = {};
38700         e.stopEvent();
38701         this.fireEvent("beforeclose", this, o);
38702         if(o.cancel !== true){
38703             this.tabPanel.removeTab(this.id);
38704         }
38705     },
38706     /**
38707      * The text displayed in the tooltip for the close icon.
38708      * @type String
38709      */
38710     closeText : "Close this tab"
38711 });
38712 /**
38713 *    This script refer to:
38714 *    Title: International Telephone Input
38715 *    Author: Jack O'Connor
38716 *    Code version:  v12.1.12
38717 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38718 **/
38719
38720 Roo.bootstrap.PhoneInputData = function() {
38721     var d = [
38722       [
38723         "Afghanistan (‫افغانستان‬‎)",
38724         "af",
38725         "93"
38726       ],
38727       [
38728         "Albania (Shqipëri)",
38729         "al",
38730         "355"
38731       ],
38732       [
38733         "Algeria (‫الجزائر‬‎)",
38734         "dz",
38735         "213"
38736       ],
38737       [
38738         "American Samoa",
38739         "as",
38740         "1684"
38741       ],
38742       [
38743         "Andorra",
38744         "ad",
38745         "376"
38746       ],
38747       [
38748         "Angola",
38749         "ao",
38750         "244"
38751       ],
38752       [
38753         "Anguilla",
38754         "ai",
38755         "1264"
38756       ],
38757       [
38758         "Antigua and Barbuda",
38759         "ag",
38760         "1268"
38761       ],
38762       [
38763         "Argentina",
38764         "ar",
38765         "54"
38766       ],
38767       [
38768         "Armenia (Հայաստան)",
38769         "am",
38770         "374"
38771       ],
38772       [
38773         "Aruba",
38774         "aw",
38775         "297"
38776       ],
38777       [
38778         "Australia",
38779         "au",
38780         "61",
38781         0
38782       ],
38783       [
38784         "Austria (Österreich)",
38785         "at",
38786         "43"
38787       ],
38788       [
38789         "Azerbaijan (Azərbaycan)",
38790         "az",
38791         "994"
38792       ],
38793       [
38794         "Bahamas",
38795         "bs",
38796         "1242"
38797       ],
38798       [
38799         "Bahrain (‫البحرين‬‎)",
38800         "bh",
38801         "973"
38802       ],
38803       [
38804         "Bangladesh (বাংলাদেশ)",
38805         "bd",
38806         "880"
38807       ],
38808       [
38809         "Barbados",
38810         "bb",
38811         "1246"
38812       ],
38813       [
38814         "Belarus (Беларусь)",
38815         "by",
38816         "375"
38817       ],
38818       [
38819         "Belgium (België)",
38820         "be",
38821         "32"
38822       ],
38823       [
38824         "Belize",
38825         "bz",
38826         "501"
38827       ],
38828       [
38829         "Benin (Bénin)",
38830         "bj",
38831         "229"
38832       ],
38833       [
38834         "Bermuda",
38835         "bm",
38836         "1441"
38837       ],
38838       [
38839         "Bhutan (འབྲུག)",
38840         "bt",
38841         "975"
38842       ],
38843       [
38844         "Bolivia",
38845         "bo",
38846         "591"
38847       ],
38848       [
38849         "Bosnia and Herzegovina (Босна и Херцеговина)",
38850         "ba",
38851         "387"
38852       ],
38853       [
38854         "Botswana",
38855         "bw",
38856         "267"
38857       ],
38858       [
38859         "Brazil (Brasil)",
38860         "br",
38861         "55"
38862       ],
38863       [
38864         "British Indian Ocean Territory",
38865         "io",
38866         "246"
38867       ],
38868       [
38869         "British Virgin Islands",
38870         "vg",
38871         "1284"
38872       ],
38873       [
38874         "Brunei",
38875         "bn",
38876         "673"
38877       ],
38878       [
38879         "Bulgaria (България)",
38880         "bg",
38881         "359"
38882       ],
38883       [
38884         "Burkina Faso",
38885         "bf",
38886         "226"
38887       ],
38888       [
38889         "Burundi (Uburundi)",
38890         "bi",
38891         "257"
38892       ],
38893       [
38894         "Cambodia (កម្ពុជា)",
38895         "kh",
38896         "855"
38897       ],
38898       [
38899         "Cameroon (Cameroun)",
38900         "cm",
38901         "237"
38902       ],
38903       [
38904         "Canada",
38905         "ca",
38906         "1",
38907         1,
38908         ["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"]
38909       ],
38910       [
38911         "Cape Verde (Kabu Verdi)",
38912         "cv",
38913         "238"
38914       ],
38915       [
38916         "Caribbean Netherlands",
38917         "bq",
38918         "599",
38919         1
38920       ],
38921       [
38922         "Cayman Islands",
38923         "ky",
38924         "1345"
38925       ],
38926       [
38927         "Central African Republic (République centrafricaine)",
38928         "cf",
38929         "236"
38930       ],
38931       [
38932         "Chad (Tchad)",
38933         "td",
38934         "235"
38935       ],
38936       [
38937         "Chile",
38938         "cl",
38939         "56"
38940       ],
38941       [
38942         "China (中国)",
38943         "cn",
38944         "86"
38945       ],
38946       [
38947         "Christmas Island",
38948         "cx",
38949         "61",
38950         2
38951       ],
38952       [
38953         "Cocos (Keeling) Islands",
38954         "cc",
38955         "61",
38956         1
38957       ],
38958       [
38959         "Colombia",
38960         "co",
38961         "57"
38962       ],
38963       [
38964         "Comoros (‫جزر القمر‬‎)",
38965         "km",
38966         "269"
38967       ],
38968       [
38969         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38970         "cd",
38971         "243"
38972       ],
38973       [
38974         "Congo (Republic) (Congo-Brazzaville)",
38975         "cg",
38976         "242"
38977       ],
38978       [
38979         "Cook Islands",
38980         "ck",
38981         "682"
38982       ],
38983       [
38984         "Costa Rica",
38985         "cr",
38986         "506"
38987       ],
38988       [
38989         "Côte d’Ivoire",
38990         "ci",
38991         "225"
38992       ],
38993       [
38994         "Croatia (Hrvatska)",
38995         "hr",
38996         "385"
38997       ],
38998       [
38999         "Cuba",
39000         "cu",
39001         "53"
39002       ],
39003       [
39004         "Curaçao",
39005         "cw",
39006         "599",
39007         0
39008       ],
39009       [
39010         "Cyprus (Κύπρος)",
39011         "cy",
39012         "357"
39013       ],
39014       [
39015         "Czech Republic (Česká republika)",
39016         "cz",
39017         "420"
39018       ],
39019       [
39020         "Denmark (Danmark)",
39021         "dk",
39022         "45"
39023       ],
39024       [
39025         "Djibouti",
39026         "dj",
39027         "253"
39028       ],
39029       [
39030         "Dominica",
39031         "dm",
39032         "1767"
39033       ],
39034       [
39035         "Dominican Republic (República Dominicana)",
39036         "do",
39037         "1",
39038         2,
39039         ["809", "829", "849"]
39040       ],
39041       [
39042         "Ecuador",
39043         "ec",
39044         "593"
39045       ],
39046       [
39047         "Egypt (‫مصر‬‎)",
39048         "eg",
39049         "20"
39050       ],
39051       [
39052         "El Salvador",
39053         "sv",
39054         "503"
39055       ],
39056       [
39057         "Equatorial Guinea (Guinea Ecuatorial)",
39058         "gq",
39059         "240"
39060       ],
39061       [
39062         "Eritrea",
39063         "er",
39064         "291"
39065       ],
39066       [
39067         "Estonia (Eesti)",
39068         "ee",
39069         "372"
39070       ],
39071       [
39072         "Ethiopia",
39073         "et",
39074         "251"
39075       ],
39076       [
39077         "Falkland Islands (Islas Malvinas)",
39078         "fk",
39079         "500"
39080       ],
39081       [
39082         "Faroe Islands (Føroyar)",
39083         "fo",
39084         "298"
39085       ],
39086       [
39087         "Fiji",
39088         "fj",
39089         "679"
39090       ],
39091       [
39092         "Finland (Suomi)",
39093         "fi",
39094         "358",
39095         0
39096       ],
39097       [
39098         "France",
39099         "fr",
39100         "33"
39101       ],
39102       [
39103         "French Guiana (Guyane française)",
39104         "gf",
39105         "594"
39106       ],
39107       [
39108         "French Polynesia (Polynésie française)",
39109         "pf",
39110         "689"
39111       ],
39112       [
39113         "Gabon",
39114         "ga",
39115         "241"
39116       ],
39117       [
39118         "Gambia",
39119         "gm",
39120         "220"
39121       ],
39122       [
39123         "Georgia (საქართველო)",
39124         "ge",
39125         "995"
39126       ],
39127       [
39128         "Germany (Deutschland)",
39129         "de",
39130         "49"
39131       ],
39132       [
39133         "Ghana (Gaana)",
39134         "gh",
39135         "233"
39136       ],
39137       [
39138         "Gibraltar",
39139         "gi",
39140         "350"
39141       ],
39142       [
39143         "Greece (Ελλάδα)",
39144         "gr",
39145         "30"
39146       ],
39147       [
39148         "Greenland (Kalaallit Nunaat)",
39149         "gl",
39150         "299"
39151       ],
39152       [
39153         "Grenada",
39154         "gd",
39155         "1473"
39156       ],
39157       [
39158         "Guadeloupe",
39159         "gp",
39160         "590",
39161         0
39162       ],
39163       [
39164         "Guam",
39165         "gu",
39166         "1671"
39167       ],
39168       [
39169         "Guatemala",
39170         "gt",
39171         "502"
39172       ],
39173       [
39174         "Guernsey",
39175         "gg",
39176         "44",
39177         1
39178       ],
39179       [
39180         "Guinea (Guinée)",
39181         "gn",
39182         "224"
39183       ],
39184       [
39185         "Guinea-Bissau (Guiné Bissau)",
39186         "gw",
39187         "245"
39188       ],
39189       [
39190         "Guyana",
39191         "gy",
39192         "592"
39193       ],
39194       [
39195         "Haiti",
39196         "ht",
39197         "509"
39198       ],
39199       [
39200         "Honduras",
39201         "hn",
39202         "504"
39203       ],
39204       [
39205         "Hong Kong (香港)",
39206         "hk",
39207         "852"
39208       ],
39209       [
39210         "Hungary (Magyarország)",
39211         "hu",
39212         "36"
39213       ],
39214       [
39215         "Iceland (Ísland)",
39216         "is",
39217         "354"
39218       ],
39219       [
39220         "India (भारत)",
39221         "in",
39222         "91"
39223       ],
39224       [
39225         "Indonesia",
39226         "id",
39227         "62"
39228       ],
39229       [
39230         "Iran (‫ایران‬‎)",
39231         "ir",
39232         "98"
39233       ],
39234       [
39235         "Iraq (‫العراق‬‎)",
39236         "iq",
39237         "964"
39238       ],
39239       [
39240         "Ireland",
39241         "ie",
39242         "353"
39243       ],
39244       [
39245         "Isle of Man",
39246         "im",
39247         "44",
39248         2
39249       ],
39250       [
39251         "Israel (‫ישראל‬‎)",
39252         "il",
39253         "972"
39254       ],
39255       [
39256         "Italy (Italia)",
39257         "it",
39258         "39",
39259         0
39260       ],
39261       [
39262         "Jamaica",
39263         "jm",
39264         "1876"
39265       ],
39266       [
39267         "Japan (日本)",
39268         "jp",
39269         "81"
39270       ],
39271       [
39272         "Jersey",
39273         "je",
39274         "44",
39275         3
39276       ],
39277       [
39278         "Jordan (‫الأردن‬‎)",
39279         "jo",
39280         "962"
39281       ],
39282       [
39283         "Kazakhstan (Казахстан)",
39284         "kz",
39285         "7",
39286         1
39287       ],
39288       [
39289         "Kenya",
39290         "ke",
39291         "254"
39292       ],
39293       [
39294         "Kiribati",
39295         "ki",
39296         "686"
39297       ],
39298       [
39299         "Kosovo",
39300         "xk",
39301         "383"
39302       ],
39303       [
39304         "Kuwait (‫الكويت‬‎)",
39305         "kw",
39306         "965"
39307       ],
39308       [
39309         "Kyrgyzstan (Кыргызстан)",
39310         "kg",
39311         "996"
39312       ],
39313       [
39314         "Laos (ລາວ)",
39315         "la",
39316         "856"
39317       ],
39318       [
39319         "Latvia (Latvija)",
39320         "lv",
39321         "371"
39322       ],
39323       [
39324         "Lebanon (‫لبنان‬‎)",
39325         "lb",
39326         "961"
39327       ],
39328       [
39329         "Lesotho",
39330         "ls",
39331         "266"
39332       ],
39333       [
39334         "Liberia",
39335         "lr",
39336         "231"
39337       ],
39338       [
39339         "Libya (‫ليبيا‬‎)",
39340         "ly",
39341         "218"
39342       ],
39343       [
39344         "Liechtenstein",
39345         "li",
39346         "423"
39347       ],
39348       [
39349         "Lithuania (Lietuva)",
39350         "lt",
39351         "370"
39352       ],
39353       [
39354         "Luxembourg",
39355         "lu",
39356         "352"
39357       ],
39358       [
39359         "Macau (澳門)",
39360         "mo",
39361         "853"
39362       ],
39363       [
39364         "Macedonia (FYROM) (Македонија)",
39365         "mk",
39366         "389"
39367       ],
39368       [
39369         "Madagascar (Madagasikara)",
39370         "mg",
39371         "261"
39372       ],
39373       [
39374         "Malawi",
39375         "mw",
39376         "265"
39377       ],
39378       [
39379         "Malaysia",
39380         "my",
39381         "60"
39382       ],
39383       [
39384         "Maldives",
39385         "mv",
39386         "960"
39387       ],
39388       [
39389         "Mali",
39390         "ml",
39391         "223"
39392       ],
39393       [
39394         "Malta",
39395         "mt",
39396         "356"
39397       ],
39398       [
39399         "Marshall Islands",
39400         "mh",
39401         "692"
39402       ],
39403       [
39404         "Martinique",
39405         "mq",
39406         "596"
39407       ],
39408       [
39409         "Mauritania (‫موريتانيا‬‎)",
39410         "mr",
39411         "222"
39412       ],
39413       [
39414         "Mauritius (Moris)",
39415         "mu",
39416         "230"
39417       ],
39418       [
39419         "Mayotte",
39420         "yt",
39421         "262",
39422         1
39423       ],
39424       [
39425         "Mexico (México)",
39426         "mx",
39427         "52"
39428       ],
39429       [
39430         "Micronesia",
39431         "fm",
39432         "691"
39433       ],
39434       [
39435         "Moldova (Republica Moldova)",
39436         "md",
39437         "373"
39438       ],
39439       [
39440         "Monaco",
39441         "mc",
39442         "377"
39443       ],
39444       [
39445         "Mongolia (Монгол)",
39446         "mn",
39447         "976"
39448       ],
39449       [
39450         "Montenegro (Crna Gora)",
39451         "me",
39452         "382"
39453       ],
39454       [
39455         "Montserrat",
39456         "ms",
39457         "1664"
39458       ],
39459       [
39460         "Morocco (‫المغرب‬‎)",
39461         "ma",
39462         "212",
39463         0
39464       ],
39465       [
39466         "Mozambique (Moçambique)",
39467         "mz",
39468         "258"
39469       ],
39470       [
39471         "Myanmar (Burma) (မြန်မာ)",
39472         "mm",
39473         "95"
39474       ],
39475       [
39476         "Namibia (Namibië)",
39477         "na",
39478         "264"
39479       ],
39480       [
39481         "Nauru",
39482         "nr",
39483         "674"
39484       ],
39485       [
39486         "Nepal (नेपाल)",
39487         "np",
39488         "977"
39489       ],
39490       [
39491         "Netherlands (Nederland)",
39492         "nl",
39493         "31"
39494       ],
39495       [
39496         "New Caledonia (Nouvelle-Calédonie)",
39497         "nc",
39498         "687"
39499       ],
39500       [
39501         "New Zealand",
39502         "nz",
39503         "64"
39504       ],
39505       [
39506         "Nicaragua",
39507         "ni",
39508         "505"
39509       ],
39510       [
39511         "Niger (Nijar)",
39512         "ne",
39513         "227"
39514       ],
39515       [
39516         "Nigeria",
39517         "ng",
39518         "234"
39519       ],
39520       [
39521         "Niue",
39522         "nu",
39523         "683"
39524       ],
39525       [
39526         "Norfolk Island",
39527         "nf",
39528         "672"
39529       ],
39530       [
39531         "North Korea (조선 민주주의 인민 공화국)",
39532         "kp",
39533         "850"
39534       ],
39535       [
39536         "Northern Mariana Islands",
39537         "mp",
39538         "1670"
39539       ],
39540       [
39541         "Norway (Norge)",
39542         "no",
39543         "47",
39544         0
39545       ],
39546       [
39547         "Oman (‫عُمان‬‎)",
39548         "om",
39549         "968"
39550       ],
39551       [
39552         "Pakistan (‫پاکستان‬‎)",
39553         "pk",
39554         "92"
39555       ],
39556       [
39557         "Palau",
39558         "pw",
39559         "680"
39560       ],
39561       [
39562         "Palestine (‫فلسطين‬‎)",
39563         "ps",
39564         "970"
39565       ],
39566       [
39567         "Panama (Panamá)",
39568         "pa",
39569         "507"
39570       ],
39571       [
39572         "Papua New Guinea",
39573         "pg",
39574         "675"
39575       ],
39576       [
39577         "Paraguay",
39578         "py",
39579         "595"
39580       ],
39581       [
39582         "Peru (Perú)",
39583         "pe",
39584         "51"
39585       ],
39586       [
39587         "Philippines",
39588         "ph",
39589         "63"
39590       ],
39591       [
39592         "Poland (Polska)",
39593         "pl",
39594         "48"
39595       ],
39596       [
39597         "Portugal",
39598         "pt",
39599         "351"
39600       ],
39601       [
39602         "Puerto Rico",
39603         "pr",
39604         "1",
39605         3,
39606         ["787", "939"]
39607       ],
39608       [
39609         "Qatar (‫قطر‬‎)",
39610         "qa",
39611         "974"
39612       ],
39613       [
39614         "Réunion (La Réunion)",
39615         "re",
39616         "262",
39617         0
39618       ],
39619       [
39620         "Romania (România)",
39621         "ro",
39622         "40"
39623       ],
39624       [
39625         "Russia (Россия)",
39626         "ru",
39627         "7",
39628         0
39629       ],
39630       [
39631         "Rwanda",
39632         "rw",
39633         "250"
39634       ],
39635       [
39636         "Saint Barthélemy",
39637         "bl",
39638         "590",
39639         1
39640       ],
39641       [
39642         "Saint Helena",
39643         "sh",
39644         "290"
39645       ],
39646       [
39647         "Saint Kitts and Nevis",
39648         "kn",
39649         "1869"
39650       ],
39651       [
39652         "Saint Lucia",
39653         "lc",
39654         "1758"
39655       ],
39656       [
39657         "Saint Martin (Saint-Martin (partie française))",
39658         "mf",
39659         "590",
39660         2
39661       ],
39662       [
39663         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39664         "pm",
39665         "508"
39666       ],
39667       [
39668         "Saint Vincent and the Grenadines",
39669         "vc",
39670         "1784"
39671       ],
39672       [
39673         "Samoa",
39674         "ws",
39675         "685"
39676       ],
39677       [
39678         "San Marino",
39679         "sm",
39680         "378"
39681       ],
39682       [
39683         "São Tomé and Príncipe (São Tomé e Príncipe)",
39684         "st",
39685         "239"
39686       ],
39687       [
39688         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39689         "sa",
39690         "966"
39691       ],
39692       [
39693         "Senegal (Sénégal)",
39694         "sn",
39695         "221"
39696       ],
39697       [
39698         "Serbia (Србија)",
39699         "rs",
39700         "381"
39701       ],
39702       [
39703         "Seychelles",
39704         "sc",
39705         "248"
39706       ],
39707       [
39708         "Sierra Leone",
39709         "sl",
39710         "232"
39711       ],
39712       [
39713         "Singapore",
39714         "sg",
39715         "65"
39716       ],
39717       [
39718         "Sint Maarten",
39719         "sx",
39720         "1721"
39721       ],
39722       [
39723         "Slovakia (Slovensko)",
39724         "sk",
39725         "421"
39726       ],
39727       [
39728         "Slovenia (Slovenija)",
39729         "si",
39730         "386"
39731       ],
39732       [
39733         "Solomon Islands",
39734         "sb",
39735         "677"
39736       ],
39737       [
39738         "Somalia (Soomaaliya)",
39739         "so",
39740         "252"
39741       ],
39742       [
39743         "South Africa",
39744         "za",
39745         "27"
39746       ],
39747       [
39748         "South Korea (대한민국)",
39749         "kr",
39750         "82"
39751       ],
39752       [
39753         "South Sudan (‫جنوب السودان‬‎)",
39754         "ss",
39755         "211"
39756       ],
39757       [
39758         "Spain (España)",
39759         "es",
39760         "34"
39761       ],
39762       [
39763         "Sri Lanka (ශ්‍රී ලංකාව)",
39764         "lk",
39765         "94"
39766       ],
39767       [
39768         "Sudan (‫السودان‬‎)",
39769         "sd",
39770         "249"
39771       ],
39772       [
39773         "Suriname",
39774         "sr",
39775         "597"
39776       ],
39777       [
39778         "Svalbard and Jan Mayen",
39779         "sj",
39780         "47",
39781         1
39782       ],
39783       [
39784         "Swaziland",
39785         "sz",
39786         "268"
39787       ],
39788       [
39789         "Sweden (Sverige)",
39790         "se",
39791         "46"
39792       ],
39793       [
39794         "Switzerland (Schweiz)",
39795         "ch",
39796         "41"
39797       ],
39798       [
39799         "Syria (‫سوريا‬‎)",
39800         "sy",
39801         "963"
39802       ],
39803       [
39804         "Taiwan (台灣)",
39805         "tw",
39806         "886"
39807       ],
39808       [
39809         "Tajikistan",
39810         "tj",
39811         "992"
39812       ],
39813       [
39814         "Tanzania",
39815         "tz",
39816         "255"
39817       ],
39818       [
39819         "Thailand (ไทย)",
39820         "th",
39821         "66"
39822       ],
39823       [
39824         "Timor-Leste",
39825         "tl",
39826         "670"
39827       ],
39828       [
39829         "Togo",
39830         "tg",
39831         "228"
39832       ],
39833       [
39834         "Tokelau",
39835         "tk",
39836         "690"
39837       ],
39838       [
39839         "Tonga",
39840         "to",
39841         "676"
39842       ],
39843       [
39844         "Trinidad and Tobago",
39845         "tt",
39846         "1868"
39847       ],
39848       [
39849         "Tunisia (‫تونس‬‎)",
39850         "tn",
39851         "216"
39852       ],
39853       [
39854         "Turkey (Türkiye)",
39855         "tr",
39856         "90"
39857       ],
39858       [
39859         "Turkmenistan",
39860         "tm",
39861         "993"
39862       ],
39863       [
39864         "Turks and Caicos Islands",
39865         "tc",
39866         "1649"
39867       ],
39868       [
39869         "Tuvalu",
39870         "tv",
39871         "688"
39872       ],
39873       [
39874         "U.S. Virgin Islands",
39875         "vi",
39876         "1340"
39877       ],
39878       [
39879         "Uganda",
39880         "ug",
39881         "256"
39882       ],
39883       [
39884         "Ukraine (Україна)",
39885         "ua",
39886         "380"
39887       ],
39888       [
39889         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39890         "ae",
39891         "971"
39892       ],
39893       [
39894         "United Kingdom",
39895         "gb",
39896         "44",
39897         0
39898       ],
39899       [
39900         "United States",
39901         "us",
39902         "1",
39903         0
39904       ],
39905       [
39906         "Uruguay",
39907         "uy",
39908         "598"
39909       ],
39910       [
39911         "Uzbekistan (Oʻzbekiston)",
39912         "uz",
39913         "998"
39914       ],
39915       [
39916         "Vanuatu",
39917         "vu",
39918         "678"
39919       ],
39920       [
39921         "Vatican City (Città del Vaticano)",
39922         "va",
39923         "39",
39924         1
39925       ],
39926       [
39927         "Venezuela",
39928         "ve",
39929         "58"
39930       ],
39931       [
39932         "Vietnam (Việt Nam)",
39933         "vn",
39934         "84"
39935       ],
39936       [
39937         "Wallis and Futuna (Wallis-et-Futuna)",
39938         "wf",
39939         "681"
39940       ],
39941       [
39942         "Western Sahara (‫الصحراء الغربية‬‎)",
39943         "eh",
39944         "212",
39945         1
39946       ],
39947       [
39948         "Yemen (‫اليمن‬‎)",
39949         "ye",
39950         "967"
39951       ],
39952       [
39953         "Zambia",
39954         "zm",
39955         "260"
39956       ],
39957       [
39958         "Zimbabwe",
39959         "zw",
39960         "263"
39961       ],
39962       [
39963         "Åland Islands",
39964         "ax",
39965         "358",
39966         1
39967       ]
39968   ];
39969   
39970   return d;
39971 }/**
39972 *    This script refer to:
39973 *    Title: International Telephone Input
39974 *    Author: Jack O'Connor
39975 *    Code version:  v12.1.12
39976 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39977 **/
39978
39979 /**
39980  * @class Roo.bootstrap.PhoneInput
39981  * @extends Roo.bootstrap.TriggerField
39982  * An input with International dial-code selection
39983  
39984  * @cfg {String} defaultDialCode default '+852'
39985  * @cfg {Array} preferedCountries default []
39986   
39987  * @constructor
39988  * Create a new PhoneInput.
39989  * @param {Object} config Configuration options
39990  */
39991
39992 Roo.bootstrap.PhoneInput = function(config) {
39993     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39994 };
39995
39996 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39997         
39998         listWidth: undefined,
39999         
40000         selectedClass: 'active',
40001         
40002         invalidClass : "has-warning",
40003         
40004         validClass: 'has-success',
40005         
40006         allowed: '0123456789',
40007         
40008         max_length: 15,
40009         
40010         /**
40011          * @cfg {String} defaultDialCode The default dial code when initializing the input
40012          */
40013         defaultDialCode: '+852',
40014         
40015         /**
40016          * @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
40017          */
40018         preferedCountries: false,
40019         
40020         getAutoCreate : function()
40021         {
40022             var data = Roo.bootstrap.PhoneInputData();
40023             var align = this.labelAlign || this.parentLabelAlign();
40024             var id = Roo.id();
40025             
40026             this.allCountries = [];
40027             this.dialCodeMapping = [];
40028             
40029             for (var i = 0; i < data.length; i++) {
40030               var c = data[i];
40031               this.allCountries[i] = {
40032                 name: c[0],
40033                 iso2: c[1],
40034                 dialCode: c[2],
40035                 priority: c[3] || 0,
40036                 areaCodes: c[4] || null
40037               };
40038               this.dialCodeMapping[c[2]] = {
40039                   name: c[0],
40040                   iso2: c[1],
40041                   priority: c[3] || 0,
40042                   areaCodes: c[4] || null
40043               };
40044             }
40045             
40046             var cfg = {
40047                 cls: 'form-group',
40048                 cn: []
40049             };
40050             
40051             var input =  {
40052                 tag: 'input',
40053                 id : id,
40054                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40055                 maxlength: this.max_length,
40056                 cls : 'form-control tel-input',
40057                 autocomplete: 'new-password'
40058             };
40059             
40060             var hiddenInput = {
40061                 tag: 'input',
40062                 type: 'hidden',
40063                 cls: 'hidden-tel-input'
40064             };
40065             
40066             if (this.name) {
40067                 hiddenInput.name = this.name;
40068             }
40069             
40070             if (this.disabled) {
40071                 input.disabled = true;
40072             }
40073             
40074             var flag_container = {
40075                 tag: 'div',
40076                 cls: 'flag-box',
40077                 cn: [
40078                     {
40079                         tag: 'div',
40080                         cls: 'flag'
40081                     },
40082                     {
40083                         tag: 'div',
40084                         cls: 'caret'
40085                     }
40086                 ]
40087             };
40088             
40089             var box = {
40090                 tag: 'div',
40091                 cls: this.hasFeedback ? 'has-feedback' : '',
40092                 cn: [
40093                     hiddenInput,
40094                     input,
40095                     {
40096                         tag: 'input',
40097                         cls: 'dial-code-holder',
40098                         disabled: true
40099                     }
40100                 ]
40101             };
40102             
40103             var container = {
40104                 cls: 'roo-select2-container input-group',
40105                 cn: [
40106                     flag_container,
40107                     box
40108                 ]
40109             };
40110             
40111             if (this.fieldLabel.length) {
40112                 var indicator = {
40113                     tag: 'i',
40114                     tooltip: 'This field is required'
40115                 };
40116                 
40117                 var label = {
40118                     tag: 'label',
40119                     'for':  id,
40120                     cls: 'control-label',
40121                     cn: []
40122                 };
40123                 
40124                 var label_text = {
40125                     tag: 'span',
40126                     html: this.fieldLabel
40127                 };
40128                 
40129                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40130                 label.cn = [
40131                     indicator,
40132                     label_text
40133                 ];
40134                 
40135                 if(this.indicatorpos == 'right') {
40136                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40137                     label.cn = [
40138                         label_text,
40139                         indicator
40140                     ];
40141                 }
40142                 
40143                 if(align == 'left') {
40144                     container = {
40145                         tag: 'div',
40146                         cn: [
40147                             container
40148                         ]
40149                     };
40150                     
40151                     if(this.labelWidth > 12){
40152                         label.style = "width: " + this.labelWidth + 'px';
40153                     }
40154                     if(this.labelWidth < 13 && this.labelmd == 0){
40155                         this.labelmd = this.labelWidth;
40156                     }
40157                     if(this.labellg > 0){
40158                         label.cls += ' col-lg-' + this.labellg;
40159                         input.cls += ' col-lg-' + (12 - this.labellg);
40160                     }
40161                     if(this.labelmd > 0){
40162                         label.cls += ' col-md-' + this.labelmd;
40163                         container.cls += ' col-md-' + (12 - this.labelmd);
40164                     }
40165                     if(this.labelsm > 0){
40166                         label.cls += ' col-sm-' + this.labelsm;
40167                         container.cls += ' col-sm-' + (12 - this.labelsm);
40168                     }
40169                     if(this.labelxs > 0){
40170                         label.cls += ' col-xs-' + this.labelxs;
40171                         container.cls += ' col-xs-' + (12 - this.labelxs);
40172                     }
40173                 }
40174             }
40175             
40176             cfg.cn = [
40177                 label,
40178                 container
40179             ];
40180             
40181             var settings = this;
40182             
40183             ['xs','sm','md','lg'].map(function(size){
40184                 if (settings[size]) {
40185                     cfg.cls += ' col-' + size + '-' + settings[size];
40186                 }
40187             });
40188             
40189             this.store = new Roo.data.Store({
40190                 proxy : new Roo.data.MemoryProxy({}),
40191                 reader : new Roo.data.JsonReader({
40192                     fields : [
40193                         {
40194                             'name' : 'name',
40195                             'type' : 'string'
40196                         },
40197                         {
40198                             'name' : 'iso2',
40199                             'type' : 'string'
40200                         },
40201                         {
40202                             'name' : 'dialCode',
40203                             'type' : 'string'
40204                         },
40205                         {
40206                             'name' : 'priority',
40207                             'type' : 'string'
40208                         },
40209                         {
40210                             'name' : 'areaCodes',
40211                             'type' : 'string'
40212                         }
40213                     ]
40214                 })
40215             });
40216             
40217             if(!this.preferedCountries) {
40218                 this.preferedCountries = [
40219                     'hk',
40220                     'gb',
40221                     'us'
40222                 ];
40223             }
40224             
40225             var p = this.preferedCountries.reverse();
40226             
40227             if(p) {
40228                 for (var i = 0; i < p.length; i++) {
40229                     for (var j = 0; j < this.allCountries.length; j++) {
40230                         if(this.allCountries[j].iso2 == p[i]) {
40231                             var t = this.allCountries[j];
40232                             this.allCountries.splice(j,1);
40233                             this.allCountries.unshift(t);
40234                         }
40235                     } 
40236                 }
40237             }
40238             
40239             this.store.proxy.data = {
40240                 success: true,
40241                 data: this.allCountries
40242             };
40243             
40244             return cfg;
40245         },
40246         
40247         initEvents : function()
40248         {
40249             this.createList();
40250             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40251             
40252             this.indicator = this.indicatorEl();
40253             this.flag = this.flagEl();
40254             this.dialCodeHolder = this.dialCodeHolderEl();
40255             
40256             this.trigger = this.el.select('div.flag-box',true).first();
40257             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40258             
40259             var _this = this;
40260             
40261             (function(){
40262                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40263                 _this.list.setWidth(lw);
40264             }).defer(100);
40265             
40266             this.list.on('mouseover', this.onViewOver, this);
40267             this.list.on('mousemove', this.onViewMove, this);
40268             this.inputEl().on("keyup", this.onKeyUp, this);
40269             this.inputEl().on("keypress", this.onKeyPress, this);
40270             
40271             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40272
40273             this.view = new Roo.View(this.list, this.tpl, {
40274                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40275             });
40276             
40277             this.view.on('click', this.onViewClick, this);
40278             this.setValue(this.defaultDialCode);
40279         },
40280         
40281         onTriggerClick : function(e)
40282         {
40283             Roo.log('trigger click');
40284             if(this.disabled){
40285                 return;
40286             }
40287             
40288             if(this.isExpanded()){
40289                 this.collapse();
40290                 this.hasFocus = false;
40291             }else {
40292                 this.store.load({});
40293                 this.hasFocus = true;
40294                 this.expand();
40295             }
40296         },
40297         
40298         isExpanded : function()
40299         {
40300             return this.list.isVisible();
40301         },
40302         
40303         collapse : function()
40304         {
40305             if(!this.isExpanded()){
40306                 return;
40307             }
40308             this.list.hide();
40309             Roo.get(document).un('mousedown', this.collapseIf, this);
40310             Roo.get(document).un('mousewheel', this.collapseIf, this);
40311             this.fireEvent('collapse', this);
40312             this.validate();
40313         },
40314         
40315         expand : function()
40316         {
40317             Roo.log('expand');
40318
40319             if(this.isExpanded() || !this.hasFocus){
40320                 return;
40321             }
40322             
40323             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40324             this.list.setWidth(lw);
40325             
40326             this.list.show();
40327             this.restrictHeight();
40328             
40329             Roo.get(document).on('mousedown', this.collapseIf, this);
40330             Roo.get(document).on('mousewheel', this.collapseIf, this);
40331             
40332             this.fireEvent('expand', this);
40333         },
40334         
40335         restrictHeight : function()
40336         {
40337             this.list.alignTo(this.inputEl(), this.listAlign);
40338             this.list.alignTo(this.inputEl(), this.listAlign);
40339         },
40340         
40341         onViewOver : function(e, t)
40342         {
40343             if(this.inKeyMode){
40344                 return;
40345             }
40346             var item = this.view.findItemFromChild(t);
40347             
40348             if(item){
40349                 var index = this.view.indexOf(item);
40350                 this.select(index, false);
40351             }
40352         },
40353
40354         // private
40355         onViewClick : function(view, doFocus, el, e)
40356         {
40357             var index = this.view.getSelectedIndexes()[0];
40358             
40359             var r = this.store.getAt(index);
40360             
40361             if(r){
40362                 this.onSelect(r, index);
40363             }
40364             if(doFocus !== false && !this.blockFocus){
40365                 this.inputEl().focus();
40366             }
40367         },
40368         
40369         onViewMove : function(e, t)
40370         {
40371             this.inKeyMode = false;
40372         },
40373         
40374         select : function(index, scrollIntoView)
40375         {
40376             this.selectedIndex = index;
40377             this.view.select(index);
40378             if(scrollIntoView !== false){
40379                 var el = this.view.getNode(index);
40380                 if(el){
40381                     this.list.scrollChildIntoView(el, false);
40382                 }
40383             }
40384         },
40385         
40386         createList : function()
40387         {
40388             this.list = Roo.get(document.body).createChild({
40389                 tag: 'ul',
40390                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40391                 style: 'display:none'
40392             });
40393             
40394             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40395         },
40396         
40397         collapseIf : function(e)
40398         {
40399             var in_combo  = e.within(this.el);
40400             var in_list =  e.within(this.list);
40401             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40402             
40403             if (in_combo || in_list || is_list) {
40404                 return;
40405             }
40406             this.collapse();
40407         },
40408         
40409         onSelect : function(record, index)
40410         {
40411             if(this.fireEvent('beforeselect', this, record, index) !== false){
40412                 
40413                 this.setFlagClass(record.data.iso2);
40414                 this.setDialCode(record.data.dialCode);
40415                 this.hasFocus = false;
40416                 this.collapse();
40417                 this.fireEvent('select', this, record, index);
40418             }
40419         },
40420         
40421         flagEl : function()
40422         {
40423             var flag = this.el.select('div.flag',true).first();
40424             if(!flag){
40425                 return false;
40426             }
40427             return flag;
40428         },
40429         
40430         dialCodeHolderEl : function()
40431         {
40432             var d = this.el.select('input.dial-code-holder',true).first();
40433             if(!d){
40434                 return false;
40435             }
40436             return d;
40437         },
40438         
40439         setDialCode : function(v)
40440         {
40441             this.dialCodeHolder.dom.value = '+'+v;
40442         },
40443         
40444         setFlagClass : function(n)
40445         {
40446             this.flag.dom.className = 'flag '+n;
40447         },
40448         
40449         getValue : function()
40450         {
40451             var v = this.inputEl().getValue();
40452             if(this.dialCodeHolder) {
40453                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40454             }
40455             return v;
40456         },
40457         
40458         setValue : function(v)
40459         {
40460             var d = this.getDialCode(v);
40461             
40462             //invalid dial code
40463             if(v.length == 0 || !d || d.length == 0) {
40464                 if(this.rendered){
40465                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40466                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40467                 }
40468                 return;
40469             }
40470             
40471             //valid dial code
40472             this.setFlagClass(this.dialCodeMapping[d].iso2);
40473             this.setDialCode(d);
40474             this.inputEl().dom.value = v.replace('+'+d,'');
40475             this.hiddenEl().dom.value = this.getValue();
40476             
40477             this.validate();
40478         },
40479         
40480         getDialCode : function(v)
40481         {
40482             v = v ||  '';
40483             
40484             if (v.length == 0) {
40485                 return this.dialCodeHolder.dom.value;
40486             }
40487             
40488             var dialCode = "";
40489             if (v.charAt(0) != "+") {
40490                 return false;
40491             }
40492             var numericChars = "";
40493             for (var i = 1; i < v.length; i++) {
40494               var c = v.charAt(i);
40495               if (!isNaN(c)) {
40496                 numericChars += c;
40497                 if (this.dialCodeMapping[numericChars]) {
40498                   dialCode = v.substr(1, i);
40499                 }
40500                 if (numericChars.length == 4) {
40501                   break;
40502                 }
40503               }
40504             }
40505             return dialCode;
40506         },
40507         
40508         reset : function()
40509         {
40510             this.setValue(this.defaultDialCode);
40511             this.validate();
40512         },
40513         
40514         hiddenEl : function()
40515         {
40516             return this.el.select('input.hidden-tel-input',true).first();
40517         },
40518         
40519         // after setting val
40520         onKeyUp : function(e){
40521             this.setValue(this.getValue());
40522         },
40523         
40524         onKeyPress : function(e){
40525             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40526                 e.stopEvent();
40527             }
40528         }
40529         
40530 });
40531 /**
40532  * @class Roo.bootstrap.MoneyField
40533  * @extends Roo.bootstrap.ComboBox
40534  * Bootstrap MoneyField class
40535  * 
40536  * @constructor
40537  * Create a new MoneyField.
40538  * @param {Object} config Configuration options
40539  */
40540
40541 Roo.bootstrap.MoneyField = function(config) {
40542     
40543     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40544     
40545 };
40546
40547 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40548     
40549     /**
40550      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40551      */
40552     allowDecimals : true,
40553     /**
40554      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40555      */
40556     decimalSeparator : ".",
40557     /**
40558      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40559      */
40560     decimalPrecision : 0,
40561     /**
40562      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40563      */
40564     allowNegative : true,
40565     /**
40566      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40567      */
40568     allowZero: true,
40569     /**
40570      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40571      */
40572     minValue : Number.NEGATIVE_INFINITY,
40573     /**
40574      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40575      */
40576     maxValue : Number.MAX_VALUE,
40577     /**
40578      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40579      */
40580     minText : "The minimum value for this field is {0}",
40581     /**
40582      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40583      */
40584     maxText : "The maximum value for this field is {0}",
40585     /**
40586      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40587      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40588      */
40589     nanText : "{0} is not a valid number",
40590     /**
40591      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40592      */
40593     castInt : true,
40594     /**
40595      * @cfg {String} defaults currency of the MoneyField
40596      * value should be in lkey
40597      */
40598     defaultCurrency : false,
40599     /**
40600      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40601      */
40602     thousandsDelimiter : false,
40603     /**
40604      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40605      */
40606     max_length: false,
40607     
40608     inputlg : 9,
40609     inputmd : 9,
40610     inputsm : 9,
40611     inputxs : 6,
40612     
40613     store : false,
40614     
40615     getAutoCreate : function()
40616     {
40617         var align = this.labelAlign || this.parentLabelAlign();
40618         
40619         var id = Roo.id();
40620
40621         var cfg = {
40622             cls: 'form-group',
40623             cn: []
40624         };
40625
40626         var input =  {
40627             tag: 'input',
40628             id : id,
40629             cls : 'form-control roo-money-amount-input',
40630             autocomplete: 'new-password'
40631         };
40632         
40633         var hiddenInput = {
40634             tag: 'input',
40635             type: 'hidden',
40636             id: Roo.id(),
40637             cls: 'hidden-number-input'
40638         };
40639         
40640         if(this.max_length) {
40641             input.maxlength = this.max_length; 
40642         }
40643         
40644         if (this.name) {
40645             hiddenInput.name = this.name;
40646         }
40647
40648         if (this.disabled) {
40649             input.disabled = true;
40650         }
40651
40652         var clg = 12 - this.inputlg;
40653         var cmd = 12 - this.inputmd;
40654         var csm = 12 - this.inputsm;
40655         var cxs = 12 - this.inputxs;
40656         
40657         var container = {
40658             tag : 'div',
40659             cls : 'row roo-money-field',
40660             cn : [
40661                 {
40662                     tag : 'div',
40663                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40664                     cn : [
40665                         {
40666                             tag : 'div',
40667                             cls: 'roo-select2-container input-group',
40668                             cn: [
40669                                 {
40670                                     tag : 'input',
40671                                     cls : 'form-control roo-money-currency-input',
40672                                     autocomplete: 'new-password',
40673                                     readOnly : 1,
40674                                     name : this.currencyName
40675                                 },
40676                                 {
40677                                     tag :'span',
40678                                     cls : 'input-group-addon',
40679                                     cn : [
40680                                         {
40681                                             tag: 'span',
40682                                             cls: 'caret'
40683                                         }
40684                                     ]
40685                                 }
40686                             ]
40687                         }
40688                     ]
40689                 },
40690                 {
40691                     tag : 'div',
40692                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40693                     cn : [
40694                         {
40695                             tag: 'div',
40696                             cls: this.hasFeedback ? 'has-feedback' : '',
40697                             cn: [
40698                                 input
40699                             ]
40700                         }
40701                     ]
40702                 }
40703             ]
40704             
40705         };
40706         
40707         if (this.fieldLabel.length) {
40708             var indicator = {
40709                 tag: 'i',
40710                 tooltip: 'This field is required'
40711             };
40712
40713             var label = {
40714                 tag: 'label',
40715                 'for':  id,
40716                 cls: 'control-label',
40717                 cn: []
40718             };
40719
40720             var label_text = {
40721                 tag: 'span',
40722                 html: this.fieldLabel
40723             };
40724
40725             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40726             label.cn = [
40727                 indicator,
40728                 label_text
40729             ];
40730
40731             if(this.indicatorpos == 'right') {
40732                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40733                 label.cn = [
40734                     label_text,
40735                     indicator
40736                 ];
40737             }
40738
40739             if(align == 'left') {
40740                 container = {
40741                     tag: 'div',
40742                     cn: [
40743                         container
40744                     ]
40745                 };
40746
40747                 if(this.labelWidth > 12){
40748                     label.style = "width: " + this.labelWidth + 'px';
40749                 }
40750                 if(this.labelWidth < 13 && this.labelmd == 0){
40751                     this.labelmd = this.labelWidth;
40752                 }
40753                 if(this.labellg > 0){
40754                     label.cls += ' col-lg-' + this.labellg;
40755                     input.cls += ' col-lg-' + (12 - this.labellg);
40756                 }
40757                 if(this.labelmd > 0){
40758                     label.cls += ' col-md-' + this.labelmd;
40759                     container.cls += ' col-md-' + (12 - this.labelmd);
40760                 }
40761                 if(this.labelsm > 0){
40762                     label.cls += ' col-sm-' + this.labelsm;
40763                     container.cls += ' col-sm-' + (12 - this.labelsm);
40764                 }
40765                 if(this.labelxs > 0){
40766                     label.cls += ' col-xs-' + this.labelxs;
40767                     container.cls += ' col-xs-' + (12 - this.labelxs);
40768                 }
40769             }
40770         }
40771
40772         cfg.cn = [
40773             label,
40774             container,
40775             hiddenInput
40776         ];
40777         
40778         var settings = this;
40779
40780         ['xs','sm','md','lg'].map(function(size){
40781             if (settings[size]) {
40782                 cfg.cls += ' col-' + size + '-' + settings[size];
40783             }
40784         });
40785         
40786         return cfg;
40787     },
40788     
40789     initEvents : function()
40790     {
40791         this.indicator = this.indicatorEl();
40792         
40793         this.initCurrencyEvent();
40794         
40795         this.initNumberEvent();
40796     },
40797     
40798     initCurrencyEvent : function()
40799     {
40800         if (!this.store) {
40801             throw "can not find store for combo";
40802         }
40803         
40804         this.store = Roo.factory(this.store, Roo.data);
40805         this.store.parent = this;
40806         
40807         this.createList();
40808         
40809         this.triggerEl = this.el.select('.input-group-addon', true).first();
40810         
40811         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40812         
40813         var _this = this;
40814         
40815         (function(){
40816             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40817             _this.list.setWidth(lw);
40818         }).defer(100);
40819         
40820         this.list.on('mouseover', this.onViewOver, this);
40821         this.list.on('mousemove', this.onViewMove, this);
40822         this.list.on('scroll', this.onViewScroll, this);
40823         
40824         if(!this.tpl){
40825             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40826         }
40827         
40828         this.view = new Roo.View(this.list, this.tpl, {
40829             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40830         });
40831         
40832         this.view.on('click', this.onViewClick, this);
40833         
40834         this.store.on('beforeload', this.onBeforeLoad, this);
40835         this.store.on('load', this.onLoad, this);
40836         this.store.on('loadexception', this.onLoadException, this);
40837         
40838         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40839             "up" : function(e){
40840                 this.inKeyMode = true;
40841                 this.selectPrev();
40842             },
40843
40844             "down" : function(e){
40845                 if(!this.isExpanded()){
40846                     this.onTriggerClick();
40847                 }else{
40848                     this.inKeyMode = true;
40849                     this.selectNext();
40850                 }
40851             },
40852
40853             "enter" : function(e){
40854                 this.collapse();
40855                 
40856                 if(this.fireEvent("specialkey", this, e)){
40857                     this.onViewClick(false);
40858                 }
40859                 
40860                 return true;
40861             },
40862
40863             "esc" : function(e){
40864                 this.collapse();
40865             },
40866
40867             "tab" : function(e){
40868                 this.collapse();
40869                 
40870                 if(this.fireEvent("specialkey", this, e)){
40871                     this.onViewClick(false);
40872                 }
40873                 
40874                 return true;
40875             },
40876
40877             scope : this,
40878
40879             doRelay : function(foo, bar, hname){
40880                 if(hname == 'down' || this.scope.isExpanded()){
40881                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40882                 }
40883                 return true;
40884             },
40885
40886             forceKeyDown: true
40887         });
40888         
40889         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40890         
40891     },
40892     
40893     initNumberEvent : function(e)
40894     {
40895         this.inputEl().on("keydown" , this.fireKey,  this);
40896         this.inputEl().on("focus", this.onFocus,  this);
40897         this.inputEl().on("blur", this.onBlur,  this);
40898         
40899         this.inputEl().relayEvent('keyup', this);
40900         
40901         if(this.indicator){
40902             this.indicator.addClass('invisible');
40903         }
40904  
40905         this.originalValue = this.getValue();
40906         
40907         if(this.validationEvent == 'keyup'){
40908             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40909             this.inputEl().on('keyup', this.filterValidation, this);
40910         }
40911         else if(this.validationEvent !== false){
40912             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40913         }
40914         
40915         if(this.selectOnFocus){
40916             this.on("focus", this.preFocus, this);
40917             
40918         }
40919         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40920             this.inputEl().on("keypress", this.filterKeys, this);
40921         } else {
40922             this.inputEl().relayEvent('keypress', this);
40923         }
40924         
40925         var allowed = "0123456789";
40926         
40927         if(this.allowDecimals){
40928             allowed += this.decimalSeparator;
40929         }
40930         
40931         if(this.allowNegative){
40932             allowed += "-";
40933         }
40934         
40935         if(this.thousandsDelimiter) {
40936             allowed += ",";
40937         }
40938         
40939         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40940         
40941         var keyPress = function(e){
40942             
40943             var k = e.getKey();
40944             
40945             var c = e.getCharCode();
40946             
40947             if(
40948                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40949                     allowed.indexOf(String.fromCharCode(c)) === -1
40950             ){
40951                 e.stopEvent();
40952                 return;
40953             }
40954             
40955             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40956                 return;
40957             }
40958             
40959             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40960                 e.stopEvent();
40961             }
40962         };
40963         
40964         this.inputEl().on("keypress", keyPress, this);
40965         
40966     },
40967     
40968     onTriggerClick : function(e)
40969     {   
40970         if(this.disabled){
40971             return;
40972         }
40973         
40974         this.page = 0;
40975         this.loadNext = false;
40976         
40977         if(this.isExpanded()){
40978             this.collapse();
40979             return;
40980         }
40981         
40982         this.hasFocus = true;
40983         
40984         if(this.triggerAction == 'all') {
40985             this.doQuery(this.allQuery, true);
40986             return;
40987         }
40988         
40989         this.doQuery(this.getRawValue());
40990     },
40991     
40992     getCurrency : function()
40993     {   
40994         var v = this.currencyEl().getValue();
40995         
40996         return v;
40997     },
40998     
40999     restrictHeight : function()
41000     {
41001         this.list.alignTo(this.currencyEl(), this.listAlign);
41002         this.list.alignTo(this.currencyEl(), this.listAlign);
41003     },
41004     
41005     onViewClick : function(view, doFocus, el, e)
41006     {
41007         var index = this.view.getSelectedIndexes()[0];
41008         
41009         var r = this.store.getAt(index);
41010         
41011         if(r){
41012             this.onSelect(r, index);
41013         }
41014     },
41015     
41016     onSelect : function(record, index){
41017         
41018         if(this.fireEvent('beforeselect', this, record, index) !== false){
41019         
41020             this.setFromCurrencyData(index > -1 ? record.data : false);
41021             
41022             this.collapse();
41023             
41024             this.fireEvent('select', this, record, index);
41025         }
41026     },
41027     
41028     setFromCurrencyData : function(o)
41029     {
41030         var currency = '';
41031         
41032         this.lastCurrency = o;
41033         
41034         if (this.currencyField) {
41035             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41036         } else {
41037             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41038         }
41039         
41040         this.lastSelectionText = currency;
41041         
41042         //setting default currency
41043         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41044             this.setCurrency(this.defaultCurrency);
41045             return;
41046         }
41047         
41048         this.setCurrency(currency);
41049     },
41050     
41051     setFromData : function(o)
41052     {
41053         var c = {};
41054         
41055         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41056         
41057         this.setFromCurrencyData(c);
41058         
41059         var value = '';
41060         
41061         if (this.name) {
41062             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41063         } else {
41064             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41065         }
41066         
41067         this.setValue(value);
41068         
41069     },
41070     
41071     setCurrency : function(v)
41072     {   
41073         this.currencyValue = v;
41074         
41075         if(this.rendered){
41076             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41077             this.validate();
41078         }
41079     },
41080     
41081     setValue : function(v)
41082     {
41083         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41084         
41085         this.value = v;
41086         
41087         if(this.rendered){
41088             
41089             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41090             
41091             this.inputEl().dom.value = (v == '') ? '' :
41092                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41093             
41094             if(!this.allowZero && v === '0') {
41095                 this.hiddenEl().dom.value = '';
41096                 this.inputEl().dom.value = '';
41097             }
41098             
41099             this.validate();
41100         }
41101     },
41102     
41103     getRawValue : function()
41104     {
41105         var v = this.inputEl().getValue();
41106         
41107         return v;
41108     },
41109     
41110     getValue : function()
41111     {
41112         return this.fixPrecision(this.parseValue(this.getRawValue()));
41113     },
41114     
41115     parseValue : function(value)
41116     {
41117         if(this.thousandsDelimiter) {
41118             value += "";
41119             r = new RegExp(",", "g");
41120             value = value.replace(r, "");
41121         }
41122         
41123         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41124         return isNaN(value) ? '' : value;
41125         
41126     },
41127     
41128     fixPrecision : function(value)
41129     {
41130         if(this.thousandsDelimiter) {
41131             value += "";
41132             r = new RegExp(",", "g");
41133             value = value.replace(r, "");
41134         }
41135         
41136         var nan = isNaN(value);
41137         
41138         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41139             return nan ? '' : value;
41140         }
41141         return parseFloat(value).toFixed(this.decimalPrecision);
41142     },
41143     
41144     decimalPrecisionFcn : function(v)
41145     {
41146         return Math.floor(v);
41147     },
41148     
41149     validateValue : function(value)
41150     {
41151         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41152             return false;
41153         }
41154         
41155         var num = this.parseValue(value);
41156         
41157         if(isNaN(num)){
41158             this.markInvalid(String.format(this.nanText, value));
41159             return false;
41160         }
41161         
41162         if(num < this.minValue){
41163             this.markInvalid(String.format(this.minText, this.minValue));
41164             return false;
41165         }
41166         
41167         if(num > this.maxValue){
41168             this.markInvalid(String.format(this.maxText, this.maxValue));
41169             return false;
41170         }
41171         
41172         return true;
41173     },
41174     
41175     validate : function()
41176     {
41177         if(this.disabled || this.allowBlank){
41178             this.markValid();
41179             return true;
41180         }
41181         
41182         var currency = this.getCurrency();
41183         
41184         if(this.validateValue(this.getRawValue()) && currency.length){
41185             this.markValid();
41186             return true;
41187         }
41188         
41189         this.markInvalid();
41190         return false;
41191     },
41192     
41193     getName: function()
41194     {
41195         return this.name;
41196     },
41197     
41198     beforeBlur : function()
41199     {
41200         if(!this.castInt){
41201             return;
41202         }
41203         
41204         var v = this.parseValue(this.getRawValue());
41205         
41206         if(v || v == 0){
41207             this.setValue(v);
41208         }
41209     },
41210     
41211     onBlur : function()
41212     {
41213         this.beforeBlur();
41214         
41215         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41216             //this.el.removeClass(this.focusClass);
41217         }
41218         
41219         this.hasFocus = false;
41220         
41221         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41222             this.validate();
41223         }
41224         
41225         var v = this.getValue();
41226         
41227         if(String(v) !== String(this.startValue)){
41228             this.fireEvent('change', this, v, this.startValue);
41229         }
41230         
41231         this.fireEvent("blur", this);
41232     },
41233     
41234     inputEl : function()
41235     {
41236         return this.el.select('.roo-money-amount-input', true).first();
41237     },
41238     
41239     currencyEl : function()
41240     {
41241         return this.el.select('.roo-money-currency-input', true).first();
41242     },
41243     
41244     hiddenEl : function()
41245     {
41246         return this.el.select('input.hidden-number-input',true).first();
41247     }
41248     
41249 });