Roo/bootstrap/htmleditor/ToolbarStandard.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fs
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     fa: '',
653     badge: '',
654     theme: 'default',
655     inverse: false,
656     
657     toggle: false,
658     ontext: 'ON',
659     offtext: 'OFF',
660     defaulton: true,
661     preventDefault: true,
662     removeClass: false,
663     name: false,
664     target: false,
665      
666     pressed : null,
667      
668     
669     getAutoCreate : function(){
670         
671         var cfg = {
672             tag : 'button',
673             cls : 'roo-button',
674             html: ''
675         };
676         
677         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
679             this.tag = 'button';
680         } else {
681             cfg.tag = this.tag;
682         }
683         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684         
685         if (this.toggle == true) {
686             cfg={
687                 tag: 'div',
688                 cls: 'slider-frame roo-button',
689                 cn: [
690                     {
691                         tag: 'span',
692                         'data-on-text':'ON',
693                         'data-off-text':'OFF',
694                         cls: 'slider-button',
695                         html: this.offtext
696                     }
697                 ]
698             };
699             
700             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701                 cfg.cls += ' '+this.weight;
702             }
703             
704             return cfg;
705         }
706         
707         if (this.isClose) {
708             cfg.cls += ' close';
709             
710             cfg["aria-hidden"] = true;
711             
712             cfg.html = "&times;";
713             
714             return cfg;
715         }
716         
717          
718         if (this.theme==='default') {
719             cfg.cls = 'btn roo-button';
720             
721             //if (this.parentType != 'Navbar') {
722             this.weight = this.weight.length ?  this.weight : 'default';
723             //}
724             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725                 
726                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728                 cfg.cls += ' btn-' + outline + weight;
729                 if (this.weight == 'default') {
730                     // BC
731                     cfg.cls += ' btn-' + this.weight;
732                 }
733             }
734         } else if (this.theme==='glow') {
735             
736             cfg.tag = 'a';
737             cfg.cls = 'btn-glow roo-button';
738             
739             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 cfg.cls += ' ' + this.weight;
742             }
743         }
744    
745         
746         if (this.inverse) {
747             this.cls += ' inverse';
748         }
749         
750         
751         if (this.active || this.pressed === true) {
752             cfg.cls += ' active';
753         }
754         
755         if (this.disabled) {
756             cfg.disabled = 'disabled';
757         }
758         
759         if (this.items) {
760             Roo.log('changing to ul' );
761             cfg.tag = 'ul';
762             this.glyphicon = 'caret';
763             if (Roo.bootstrap.version == 4) {
764                 this.fa = 'caret-down';
765             }
766             
767         }
768         
769         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
770          
771         //gsRoo.log(this.parentType);
772         if (this.parentType === 'Navbar' && !this.parent().bar) {
773             Roo.log('changing to li?');
774             
775             cfg.tag = 'li';
776             
777             cfg.cls = '';
778             cfg.cn =  [{
779                 tag : 'a',
780                 cls : 'roo-button',
781                 html : this.html,
782                 href : this.href || '#'
783             }];
784             if (this.menu) {
785                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
786                 cfg.cls += ' dropdown';
787             }   
788             
789             delete cfg.html;
790             
791         }
792         
793        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
794         
795         if (this.glyphicon) {
796             cfg.html = ' ' + cfg.html;
797             
798             cfg.cn = [
799                 {
800                     tag: 'span',
801                     cls: 'glyphicon glyphicon-' + this.glyphicon
802                 }
803             ];
804         }
805         if (this.fa) {
806             cfg.html = ' ' + cfg.html;
807             
808             cfg.cn = [
809                 {
810                     tag: 'i',
811                     cls: 'fa fas fa-' + this.fa
812                 }
813             ];
814         }
815         
816         if (this.badge) {
817             cfg.html += ' ';
818             
819             cfg.tag = 'a';
820             
821 //            cfg.cls='btn roo-button';
822             
823             cfg.href=this.href;
824             
825             var value = cfg.html;
826             
827             if(this.glyphicon){
828                 value = {
829                     tag: 'span',
830                     cls: 'glyphicon glyphicon-' + this.glyphicon,
831                     html: this.html
832                 };
833             }
834             if(this.fa){
835                 value = {
836                     tag: 'i',
837                     cls: 'fa fas fa-' + this.fa,
838                     html: this.html
839                 };
840             }
841             
842             var bw = this.badge_weight.length ? this.badge_weight :
843                 (this.weight.length ? this.weight : 'secondary');
844             bw = bw == 'default' ? 'secondary' : bw;
845             
846             cfg.cn = [
847                 value,
848                 {
849                     tag: 'span',
850                     cls: 'badge badge-' + bw,
851                     html: this.badge
852                 }
853             ];
854             
855             cfg.html='';
856         }
857         
858         if (this.menu) {
859             cfg.cls += ' dropdown';
860             cfg.html = typeof(cfg.html) != 'undefined' ?
861                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
862         }
863         
864         if (cfg.tag !== 'a' && this.href !== '') {
865             throw "Tag must be a to set href.";
866         } else if (this.href.length > 0) {
867             cfg.href = this.href;
868         }
869         
870         if(this.removeClass){
871             cfg.cls = '';
872         }
873         
874         if(this.target){
875             cfg.target = this.target;
876         }
877         
878         return cfg;
879     },
880     initEvents: function() {
881        // Roo.log('init events?');
882 //        Roo.log(this.el.dom);
883         // add the menu...
884         
885         if (typeof (this.menu) != 'undefined') {
886             this.menu.parentType = this.xtype;
887             this.menu.triggerEl = this.el;
888             this.addxtype(Roo.apply({}, this.menu));
889         }
890
891
892        if (this.el.hasClass('roo-button')) {
893             this.el.on('click', this.onClick, this);
894        } else {
895             this.el.select('.roo-button').on('click', this.onClick, this);
896        }
897        
898        if(this.removeClass){
899            this.el.on('click', this.onClick, this);
900        }
901        
902        this.el.enableDisplayMode();
903         
904     },
905     onClick : function(e)
906     {
907         if (this.disabled) {
908             return;
909         }
910         
911         Roo.log('button on click ');
912         if(this.preventDefault){
913             e.preventDefault();
914         }
915         
916         if (this.pressed === true || this.pressed === false) {
917             this.toggleActive(e);
918         }
919         
920         
921         this.fireEvent('click', this, e);
922     },
923     
924     /**
925      * Enables this button
926      */
927     enable : function()
928     {
929         this.disabled = false;
930         this.el.removeClass('disabled');
931     },
932     
933     /**
934      * Disable this button
935      */
936     disable : function()
937     {
938         this.disabled = true;
939         this.el.addClass('disabled');
940     },
941      /**
942      * sets the active state on/off, 
943      * @param {Boolean} state (optional) Force a particular state
944      */
945     setActive : function(v) {
946         
947         this.el[v ? 'addClass' : 'removeClass']('active');
948         this.pressed = v;
949     },
950      /**
951      * toggles the current active state 
952      */
953     toggleActive : function(e)
954     {
955         this.setActive(!this.pressed);
956         this.fireEvent('toggle', this, e, !this.pressed);
957     },
958      /**
959      * get the current active state
960      * @return {boolean} true if it's active
961      */
962     isActive : function()
963     {
964         return this.el.hasClass('active');
965     },
966     /**
967      * set the text of the first selected button
968      */
969     setText : function(str)
970     {
971         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
972     },
973     /**
974      * get the text of the first selected button
975      */
976     getText : function()
977     {
978         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
979     },
980     
981     setWeight : function(str)
982     {
983         this.el.removeClass(this.weightClass);
984         this.weight = str;
985         var outline = this.outline ? 'outline-' : '';
986         if (str == 'default') {
987             this.el.addClass('btn-default btn-outline-secondary');        
988             return;
989         }
990         this.el.addClass('btn-' + outline + str);        
991     }
992     
993     
994 });
995
996  /*
997  * - LGPL
998  *
999  * column
1000  * 
1001  */
1002
1003 /**
1004  * @class Roo.bootstrap.Column
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Column class
1007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1015  *
1016  * 
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019  * @cfg {String} fa (ban|check|...) font awesome icon
1020  * @cfg {Number} fasize (1|2|....) font awsome size
1021
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023
1024  * @cfg {String} html content of column.
1025  * 
1026  * @constructor
1027  * Create a new Column
1028  * @param {Object} config The config object
1029  */
1030
1031 Roo.bootstrap.Column = function(config){
1032     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1033 };
1034
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1036     
1037     xs: false,
1038     sm: false,
1039     md: false,
1040     lg: false,
1041     xsoff: false,
1042     smoff: false,
1043     mdoff: false,
1044     lgoff: false,
1045     html: '',
1046     offset: 0,
1047     alert: false,
1048     fa: false,
1049     icon : false,
1050     hidden : false,
1051     fasize : 1,
1052     
1053     getAutoCreate : function(){
1054         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1055         
1056         cfg = {
1057             tag: 'div',
1058             cls: 'column'
1059         };
1060         
1061         var settings=this;
1062         ['xs','sm','md','lg'].map(function(size){
1063             //Roo.log( size + ':' + settings[size]);
1064             
1065             if (settings[size+'off'] !== false) {
1066                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1067             }
1068             
1069             if (settings[size] === false) {
1070                 return;
1071             }
1072             
1073             if (!settings[size]) { // 0 = hidden
1074                 cfg.cls += ' hidden-' + size;
1075                 return;
1076             }
1077             cfg.cls += ' col-' + size + '-' + settings[size];
1078             
1079         });
1080         
1081         if (this.hidden) {
1082             cfg.cls += ' hidden';
1083         }
1084         
1085         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086             cfg.cls +=' alert alert-' + this.alert;
1087         }
1088         
1089         
1090         if (this.html.length) {
1091             cfg.html = this.html;
1092         }
1093         if (this.fa) {
1094             var fasize = '';
1095             if (this.fasize > 1) {
1096                 fasize = ' fa-' + this.fasize + 'x';
1097             }
1098             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1099             
1100             
1101         }
1102         if (this.icon) {
1103             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1104         }
1105         
1106         return cfg;
1107     }
1108    
1109 });
1110
1111  
1112
1113  /*
1114  * - LGPL
1115  *
1116  * page container.
1117  * 
1118  */
1119
1120
1121 /**
1122  * @class Roo.bootstrap.Container
1123  * @extends Roo.bootstrap.Component
1124  * Bootstrap Container class
1125  * @cfg {Boolean} jumbotron is it a jumbotron element
1126  * @cfg {String} html content of element
1127  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1129  * @cfg {String} header content of header (for panel)
1130  * @cfg {String} footer content of footer (for panel)
1131  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132  * @cfg {String} tag (header|aside|section) type of HTML tag.
1133  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134  * @cfg {String} fa font awesome icon
1135  * @cfg {String} icon (info-sign|check|...) glyphicon name
1136  * @cfg {Boolean} hidden (true|false) hide the element
1137  * @cfg {Boolean} expandable (true|false) default false
1138  * @cfg {Boolean} expanded (true|false) default true
1139  * @cfg {String} rheader contet on the right of header
1140  * @cfg {Boolean} clickable (true|false) default false
1141
1142  *     
1143  * @constructor
1144  * Create a new Container
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Container = function(config){
1149     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // raw events
1153          /**
1154          * @event expand
1155          * After the panel has been expand
1156          * 
1157          * @param {Roo.bootstrap.Container} this
1158          */
1159         "expand" : true,
1160         /**
1161          * @event collapse
1162          * After the panel has been collapsed
1163          * 
1164          * @param {Roo.bootstrap.Container} this
1165          */
1166         "collapse" : true,
1167         /**
1168          * @event click
1169          * When a element is chick
1170          * @param {Roo.bootstrap.Container} this
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1178     
1179     jumbotron : false,
1180     well: '',
1181     panel : '',
1182     header: '',
1183     footer : '',
1184     sticky: '',
1185     tag : false,
1186     alert : false,
1187     fa: false,
1188     icon : false,
1189     expandable : false,
1190     rheader : '',
1191     expanded : true,
1192     clickable: false,
1193   
1194      
1195     getChildContainer : function() {
1196         
1197         if(!this.el){
1198             return false;
1199         }
1200         
1201         if (this.panel.length) {
1202             return this.el.select('.panel-body',true).first();
1203         }
1204         
1205         return this.el;
1206     },
1207     
1208     
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag : this.tag || 'div',
1213             html : '',
1214             cls : ''
1215         };
1216         if (this.jumbotron) {
1217             cfg.cls = 'jumbotron';
1218         }
1219         
1220         
1221         
1222         // - this is applied by the parent..
1223         //if (this.cls) {
1224         //    cfg.cls = this.cls + '';
1225         //}
1226         
1227         if (this.sticky.length) {
1228             
1229             var bd = Roo.get(document.body);
1230             if (!bd.hasClass('bootstrap-sticky')) {
1231                 bd.addClass('bootstrap-sticky');
1232                 Roo.select('html',true).setStyle('height', '100%');
1233             }
1234              
1235             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1236         }
1237         
1238         
1239         if (this.well.length) {
1240             switch (this.well) {
1241                 case 'lg':
1242                 case 'sm':
1243                     cfg.cls +=' well well-' +this.well;
1244                     break;
1245                 default:
1246                     cfg.cls +=' well';
1247                     break;
1248             }
1249         }
1250         
1251         if (this.hidden) {
1252             cfg.cls += ' hidden';
1253         }
1254         
1255         
1256         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257             cfg.cls +=' alert alert-' + this.alert;
1258         }
1259         
1260         var body = cfg;
1261         
1262         if (this.panel.length) {
1263             cfg.cls += ' panel panel-' + this.panel;
1264             cfg.cn = [];
1265             if (this.header.length) {
1266                 
1267                 var h = [];
1268                 
1269                 if(this.expandable){
1270                     
1271                     cfg.cls = cfg.cls + ' expandable';
1272                     
1273                     h.push({
1274                         tag: 'i',
1275                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1276                     });
1277                     
1278                 }
1279                 
1280                 h.push(
1281                     {
1282                         tag: 'span',
1283                         cls : 'panel-title',
1284                         html : (this.expandable ? '&nbsp;' : '') + this.header
1285                     },
1286                     {
1287                         tag: 'span',
1288                         cls: 'panel-header-right',
1289                         html: this.rheader
1290                     }
1291                 );
1292                 
1293                 cfg.cn.push({
1294                     cls : 'panel-heading',
1295                     style : this.expandable ? 'cursor: pointer' : '',
1296                     cn : h
1297                 });
1298                 
1299             }
1300             
1301             body = false;
1302             cfg.cn.push({
1303                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1304                 html : this.html
1305             });
1306             
1307             
1308             if (this.footer.length) {
1309                 cfg.cn.push({
1310                     cls : 'panel-footer',
1311                     html : this.footer
1312                     
1313                 });
1314             }
1315             
1316         }
1317         
1318         if (body) {
1319             body.html = this.html || cfg.html;
1320             // prefix with the icons..
1321             if (this.fa) {
1322                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1323             }
1324             if (this.icon) {
1325                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1326             }
1327             
1328             
1329         }
1330         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331             cfg.cls =  'container';
1332         }
1333         
1334         return cfg;
1335     },
1336     
1337     initEvents: function() 
1338     {
1339         if(this.expandable){
1340             var headerEl = this.headerEl();
1341         
1342             if(headerEl){
1343                 headerEl.on('click', this.onToggleClick, this);
1344             }
1345         }
1346         
1347         if(this.clickable){
1348             this.el.on('click', this.onClick, this);
1349         }
1350         
1351     },
1352     
1353     onToggleClick : function()
1354     {
1355         var headerEl = this.headerEl();
1356         
1357         if(!headerEl){
1358             return;
1359         }
1360         
1361         if(this.expanded){
1362             this.collapse();
1363             return;
1364         }
1365         
1366         this.expand();
1367     },
1368     
1369     expand : function()
1370     {
1371         if(this.fireEvent('expand', this)) {
1372             
1373             this.expanded = true;
1374             
1375             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1376             
1377             this.el.select('.panel-body',true).first().removeClass('hide');
1378             
1379             var toggleEl = this.toggleEl();
1380
1381             if(!toggleEl){
1382                 return;
1383             }
1384
1385             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1386         }
1387         
1388     },
1389     
1390     collapse : function()
1391     {
1392         if(this.fireEvent('collapse', this)) {
1393             
1394             this.expanded = false;
1395             
1396             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397             this.el.select('.panel-body',true).first().addClass('hide');
1398         
1399             var toggleEl = this.toggleEl();
1400
1401             if(!toggleEl){
1402                 return;
1403             }
1404
1405             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1406         }
1407     },
1408     
1409     toggleEl : function()
1410     {
1411         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1412             return;
1413         }
1414         
1415         return this.el.select('.panel-heading .fa',true).first();
1416     },
1417     
1418     headerEl : function()
1419     {
1420         if(!this.el || !this.panel.length || !this.header.length){
1421             return;
1422         }
1423         
1424         return this.el.select('.panel-heading',true).first()
1425     },
1426     
1427     bodyEl : function()
1428     {
1429         if(!this.el || !this.panel.length){
1430             return;
1431         }
1432         
1433         return this.el.select('.panel-body',true).first()
1434     },
1435     
1436     titleEl : function()
1437     {
1438         if(!this.el || !this.panel.length || !this.header.length){
1439             return;
1440         }
1441         
1442         return this.el.select('.panel-title',true).first();
1443     },
1444     
1445     setTitle : function(v)
1446     {
1447         var titleEl = this.titleEl();
1448         
1449         if(!titleEl){
1450             return;
1451         }
1452         
1453         titleEl.dom.innerHTML = v;
1454     },
1455     
1456     getTitle : function()
1457     {
1458         
1459         var titleEl = this.titleEl();
1460         
1461         if(!titleEl){
1462             return '';
1463         }
1464         
1465         return titleEl.dom.innerHTML;
1466     },
1467     
1468     setRightTitle : function(v)
1469     {
1470         var t = this.el.select('.panel-header-right',true).first();
1471         
1472         if(!t){
1473             return;
1474         }
1475         
1476         t.dom.innerHTML = v;
1477     },
1478     
1479     onClick : function(e)
1480     {
1481         e.preventDefault();
1482         
1483         this.fireEvent('click', this, e);
1484     }
1485 });
1486
1487  /*
1488  * - LGPL
1489  *
1490  * image
1491  * 
1492  */
1493
1494
1495 /**
1496  * @class Roo.bootstrap.Img
1497  * @extends Roo.bootstrap.Component
1498  * Bootstrap Img class
1499  * @cfg {Boolean} imgResponsive false | true
1500  * @cfg {String} border rounded | circle | thumbnail
1501  * @cfg {String} src image source
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505  * @cfg {String} xsUrl xs image source
1506  * @cfg {String} smUrl sm image source
1507  * @cfg {String} mdUrl md image source
1508  * @cfg {String} lgUrl lg image source
1509  * 
1510  * @constructor
1511  * Create a new Input
1512  * @param {Object} config The config object
1513  */
1514
1515 Roo.bootstrap.Img = function(config){
1516     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1517     
1518     this.addEvents({
1519         // img events
1520         /**
1521          * @event click
1522          * The img click event for the img.
1523          * @param {Roo.EventObject} e
1524          */
1525         "click" : true
1526     });
1527 };
1528
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1530     
1531     imgResponsive: true,
1532     border: '',
1533     src: 'about:blank',
1534     href: false,
1535     target: false,
1536     xsUrl: '',
1537     smUrl: '',
1538     mdUrl: '',
1539     lgUrl: '',
1540
1541     getAutoCreate : function()
1542     {   
1543         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544             return this.createSingleImg();
1545         }
1546         
1547         var cfg = {
1548             tag: 'div',
1549             cls: 'roo-image-responsive-group',
1550             cn: []
1551         };
1552         var _this = this;
1553         
1554         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1555             
1556             if(!_this[size + 'Url']){
1557                 return;
1558             }
1559             
1560             var img = {
1561                 tag: 'img',
1562                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563                 html: _this.html || cfg.html,
1564                 src: _this[size + 'Url']
1565             };
1566             
1567             img.cls += ' roo-image-responsive-' + size;
1568             
1569             var s = ['xs', 'sm', 'md', 'lg'];
1570             
1571             s.splice(s.indexOf(size), 1);
1572             
1573             Roo.each(s, function(ss){
1574                 img.cls += ' hidden-' + ss;
1575             });
1576             
1577             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578                 cfg.cls += ' img-' + _this.border;
1579             }
1580             
1581             if(_this.alt){
1582                 cfg.alt = _this.alt;
1583             }
1584             
1585             if(_this.href){
1586                 var a = {
1587                     tag: 'a',
1588                     href: _this.href,
1589                     cn: [
1590                         img
1591                     ]
1592                 };
1593
1594                 if(this.target){
1595                     a.target = _this.target;
1596                 }
1597             }
1598             
1599             cfg.cn.push((_this.href) ? a : img);
1600             
1601         });
1602         
1603         return cfg;
1604     },
1605     
1606     createSingleImg : function()
1607     {
1608         var cfg = {
1609             tag: 'img',
1610             cls: (this.imgResponsive) ? 'img-responsive' : '',
1611             html : null,
1612             src : 'about:blank'  // just incase src get's set to undefined?!?
1613         };
1614         
1615         cfg.html = this.html || cfg.html;
1616         
1617         cfg.src = this.src || cfg.src;
1618         
1619         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620             cfg.cls += ' img-' + this.border;
1621         }
1622         
1623         if(this.alt){
1624             cfg.alt = this.alt;
1625         }
1626         
1627         if(this.href){
1628             var a = {
1629                 tag: 'a',
1630                 href: this.href,
1631                 cn: [
1632                     cfg
1633                 ]
1634             };
1635             
1636             if(this.target){
1637                 a.target = this.target;
1638             }
1639             
1640         }
1641         
1642         return (this.href) ? a : cfg;
1643     },
1644     
1645     initEvents: function() 
1646     {
1647         if(!this.href){
1648             this.el.on('click', this.onClick, this);
1649         }
1650         
1651     },
1652     
1653     onClick : function(e)
1654     {
1655         Roo.log('img onclick');
1656         this.fireEvent('click', this, e);
1657     },
1658     /**
1659      * Sets the url of the image - used to update it
1660      * @param {String} url the url of the image
1661      */
1662     
1663     setSrc : function(url)
1664     {
1665         this.src =  url;
1666         
1667         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668             this.el.dom.src =  url;
1669             return;
1670         }
1671         
1672         this.el.select('img', true).first().dom.src =  url;
1673     }
1674     
1675     
1676    
1677 });
1678
1679  /*
1680  * - LGPL
1681  *
1682  * image
1683  * 
1684  */
1685
1686
1687 /**
1688  * @class Roo.bootstrap.Link
1689  * @extends Roo.bootstrap.Component
1690  * Bootstrap Link Class
1691  * @cfg {String} alt image alternative text
1692  * @cfg {String} href a tag href
1693  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694  * @cfg {String} html the content of the link.
1695  * @cfg {String} anchor name for the anchor link
1696  * @cfg {String} fa - favicon
1697
1698  * @cfg {Boolean} preventDefault (true | false) default false
1699
1700  * 
1701  * @constructor
1702  * Create a new Input
1703  * @param {Object} config The config object
1704  */
1705
1706 Roo.bootstrap.Link = function(config){
1707     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1708     
1709     this.addEvents({
1710         // img events
1711         /**
1712          * @event click
1713          * The img click event for the img.
1714          * @param {Roo.EventObject} e
1715          */
1716         "click" : true
1717     });
1718 };
1719
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1721     
1722     href: false,
1723     target: false,
1724     preventDefault: false,
1725     anchor : false,
1726     alt : false,
1727     fa: false,
1728
1729
1730     getAutoCreate : function()
1731     {
1732         var html = this.html || '';
1733         
1734         if (this.fa !== false) {
1735             html = '<i class="fa fa-' + this.fa + '"></i>';
1736         }
1737         var cfg = {
1738             tag: 'a'
1739         };
1740         // anchor's do not require html/href...
1741         if (this.anchor === false) {
1742             cfg.html = html;
1743             cfg.href = this.href || '#';
1744         } else {
1745             cfg.name = this.anchor;
1746             if (this.html !== false || this.fa !== false) {
1747                 cfg.html = html;
1748             }
1749             if (this.href !== false) {
1750                 cfg.href = this.href;
1751             }
1752         }
1753         
1754         if(this.alt !== false){
1755             cfg.alt = this.alt;
1756         }
1757         
1758         
1759         if(this.target !== false) {
1760             cfg.target = this.target;
1761         }
1762         
1763         return cfg;
1764     },
1765     
1766     initEvents: function() {
1767         
1768         if(!this.href || this.preventDefault){
1769             this.el.on('click', this.onClick, this);
1770         }
1771     },
1772     
1773     onClick : function(e)
1774     {
1775         if(this.preventDefault){
1776             e.preventDefault();
1777         }
1778         //Roo.log('img onclick');
1779         this.fireEvent('click', this, e);
1780     }
1781    
1782 });
1783
1784  /*
1785  * - LGPL
1786  *
1787  * header
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Header
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Header class
1795  * @cfg {String} html content of header
1796  * @cfg {Number} level (1|2|3|4|5|6) default 1
1797  * 
1798  * @constructor
1799  * Create a new Header
1800  * @param {Object} config The config object
1801  */
1802
1803
1804 Roo.bootstrap.Header  = function(config){
1805     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1806 };
1807
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1809     
1810     //href : false,
1811     html : false,
1812     level : 1,
1813     
1814     
1815     
1816     getAutoCreate : function(){
1817         
1818         
1819         
1820         var cfg = {
1821             tag: 'h' + (1 *this.level),
1822             html: this.html || ''
1823         } ;
1824         
1825         return cfg;
1826     }
1827    
1828 });
1829
1830  
1831
1832  /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842  
1843 /**
1844  * @class Roo.bootstrap.MenuMgr
1845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1846  * @singleton
1847  */
1848 Roo.bootstrap.MenuMgr = function(){
1849    var menus, active, groups = {}, attached = false, lastShow = new Date();
1850
1851    // private - called when first menu is created
1852    function init(){
1853        menus = {};
1854        active = new Roo.util.MixedCollection();
1855        Roo.get(document).addKeyListener(27, function(){
1856            if(active.length > 0){
1857                hideAll();
1858            }
1859        });
1860    }
1861
1862    // private
1863    function hideAll(){
1864        if(active && active.length > 0){
1865            var c = active.clone();
1866            c.each(function(m){
1867                m.hide();
1868            });
1869        }
1870    }
1871
1872    // private
1873    function onHide(m){
1874        active.remove(m);
1875        if(active.length < 1){
1876            Roo.get(document).un("mouseup", onMouseDown);
1877             
1878            attached = false;
1879        }
1880    }
1881
1882    // private
1883    function onShow(m){
1884        var last = active.last();
1885        lastShow = new Date();
1886        active.add(m);
1887        if(!attached){
1888           Roo.get(document).on("mouseup", onMouseDown);
1889            
1890            attached = true;
1891        }
1892        if(m.parentMenu){
1893           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894           m.parentMenu.activeChild = m;
1895        }else if(last && last.isVisible()){
1896           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1897        }
1898    }
1899
1900    // private
1901    function onBeforeHide(m){
1902        if(m.activeChild){
1903            m.activeChild.hide();
1904        }
1905        if(m.autoHideTimer){
1906            clearTimeout(m.autoHideTimer);
1907            delete m.autoHideTimer;
1908        }
1909    }
1910
1911    // private
1912    function onBeforeShow(m){
1913        var pm = m.parentMenu;
1914        if(!pm && !m.allowOtherMenus){
1915            hideAll();
1916        }else if(pm && pm.activeChild && active != m){
1917            pm.activeChild.hide();
1918        }
1919    }
1920
1921    // private this should really trigger on mouseup..
1922    function onMouseDown(e){
1923         Roo.log("on Mouse Up");
1924         
1925         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926             Roo.log("MenuManager hideAll");
1927             hideAll();
1928             e.stopEvent();
1929         }
1930         
1931         
1932    }
1933
1934    // private
1935    function onBeforeCheck(mi, state){
1936        if(state){
1937            var g = groups[mi.group];
1938            for(var i = 0, l = g.length; i < l; i++){
1939                if(g[i] != mi){
1940                    g[i].setChecked(false);
1941                }
1942            }
1943        }
1944    }
1945
1946    return {
1947
1948        /**
1949         * Hides all menus that are currently visible
1950         */
1951        hideAll : function(){
1952             hideAll();  
1953        },
1954
1955        // private
1956        register : function(menu){
1957            if(!menus){
1958                init();
1959            }
1960            menus[menu.id] = menu;
1961            menu.on("beforehide", onBeforeHide);
1962            menu.on("hide", onHide);
1963            menu.on("beforeshow", onBeforeShow);
1964            menu.on("show", onShow);
1965            var g = menu.group;
1966            if(g && menu.events["checkchange"]){
1967                if(!groups[g]){
1968                    groups[g] = [];
1969                }
1970                groups[g].push(menu);
1971                menu.on("checkchange", onCheck);
1972            }
1973        },
1974
1975         /**
1976          * Returns a {@link Roo.menu.Menu} object
1977          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978          * be used to generate and return a new Menu instance.
1979          */
1980        get : function(menu){
1981            if(typeof menu == "string"){ // menu id
1982                return menus[menu];
1983            }else if(menu.events){  // menu instance
1984                return menu;
1985            }
1986            /*else if(typeof menu.length == 'number'){ // array of menu items?
1987                return new Roo.bootstrap.Menu({items:menu});
1988            }else{ // otherwise, must be a config
1989                return new Roo.bootstrap.Menu(menu);
1990            }
1991            */
1992            return false;
1993        },
1994
1995        // private
1996        unregister : function(menu){
1997            delete menus[menu.id];
1998            menu.un("beforehide", onBeforeHide);
1999            menu.un("hide", onHide);
2000            menu.un("beforeshow", onBeforeShow);
2001            menu.un("show", onShow);
2002            var g = menu.group;
2003            if(g && menu.events["checkchange"]){
2004                groups[g].remove(menu);
2005                menu.un("checkchange", onCheck);
2006            }
2007        },
2008
2009        // private
2010        registerCheckable : function(menuItem){
2011            var g = menuItem.group;
2012            if(g){
2013                if(!groups[g]){
2014                    groups[g] = [];
2015                }
2016                groups[g].push(menuItem);
2017                menuItem.on("beforecheckchange", onBeforeCheck);
2018            }
2019        },
2020
2021        // private
2022        unregisterCheckable : function(menuItem){
2023            var g = menuItem.group;
2024            if(g){
2025                groups[g].remove(menuItem);
2026                menuItem.un("beforecheckchange", onBeforeCheck);
2027            }
2028        }
2029    };
2030 }();/*
2031  * - LGPL
2032  *
2033  * menu
2034  * 
2035  */
2036
2037 /**
2038  * @class Roo.bootstrap.Menu
2039  * @extends Roo.bootstrap.Component
2040  * Bootstrap Menu class - container for MenuItems
2041  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2043  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2044  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2045  * 
2046  * @constructor
2047  * Create a new Menu
2048  * @param {Object} config The config object
2049  */
2050
2051
2052 Roo.bootstrap.Menu = function(config){
2053     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054     if (this.registerMenu && this.type != 'treeview')  {
2055         Roo.bootstrap.MenuMgr.register(this);
2056     }
2057     
2058     
2059     this.addEvents({
2060         /**
2061          * @event beforeshow
2062          * Fires before this menu is displayed
2063          * @param {Roo.menu.Menu} this
2064          */
2065         beforeshow : true,
2066         /**
2067          * @event beforehide
2068          * Fires before this menu is hidden
2069          * @param {Roo.menu.Menu} this
2070          */
2071         beforehide : true,
2072         /**
2073          * @event show
2074          * Fires after this menu is displayed
2075          * @param {Roo.menu.Menu} this
2076          */
2077         show : true,
2078         /**
2079          * @event hide
2080          * Fires after this menu is hidden
2081          * @param {Roo.menu.Menu} this
2082          */
2083         hide : true,
2084         /**
2085          * @event click
2086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087          * @param {Roo.menu.Menu} this
2088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089          * @param {Roo.EventObject} e
2090          */
2091         click : true,
2092         /**
2093          * @event mouseover
2094          * Fires when the mouse is hovering over this menu
2095          * @param {Roo.menu.Menu} this
2096          * @param {Roo.EventObject} e
2097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2098          */
2099         mouseover : true,
2100         /**
2101          * @event mouseout
2102          * Fires when the mouse exits this menu
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.EventObject} e
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          */
2107         mouseout : true,
2108         /**
2109          * @event itemclick
2110          * Fires when a menu item contained in this menu is clicked
2111          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112          * @param {Roo.EventObject} e
2113          */
2114         itemclick: true
2115     });
2116     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2120     
2121    /// html : false,
2122     //align : '',
2123     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2124     type: false,
2125     /**
2126      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2127      */
2128     registerMenu : true,
2129     
2130     menuItems :false, // stores the menu items..
2131     
2132     hidden:true,
2133         
2134     parentMenu : false,
2135     
2136     stopEvent : true,
2137     
2138     isLink : false,
2139     
2140     getChildContainer : function() {
2141         return this.el;  
2142     },
2143     
2144     getAutoCreate : function(){
2145          
2146         //if (['right'].indexOf(this.align)!==-1) {
2147         //    cfg.cn[1].cls += ' pull-right'
2148         //}
2149         
2150         
2151         var cfg = {
2152             tag : 'ul',
2153             cls : 'dropdown-menu' ,
2154             style : 'z-index:1000'
2155             
2156         };
2157         
2158         if (this.type === 'submenu') {
2159             cfg.cls = 'submenu active';
2160         }
2161         if (this.type === 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         return cfg;
2166     },
2167     initEvents : function() {
2168         
2169        // Roo.log("ADD event");
2170        // Roo.log(this.triggerEl.dom);
2171         
2172         this.triggerEl.on('click', this.onTriggerClick, this);
2173         
2174         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2175         
2176         
2177         if (this.triggerEl.hasClass('nav-item')) {
2178             // dropdown toggle on the 'a' in BS4?
2179             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2180         } else {
2181             this.triggerEl.addClass('dropdown-toggle');
2182         }
2183         if (Roo.isTouch) {
2184             this.el.on('touchstart'  , this.onTouch, this);
2185         }
2186         this.el.on('click' , this.onClick, this);
2187
2188         this.el.on("mouseover", this.onMouseOver, this);
2189         this.el.on("mouseout", this.onMouseOut, this);
2190         
2191     },
2192     
2193     findTargetItem : function(e)
2194     {
2195         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2196         if(!t){
2197             return false;
2198         }
2199         //Roo.log(t);         Roo.log(t.id);
2200         if(t && t.id){
2201             //Roo.log(this.menuitems);
2202             return this.menuitems.get(t.id);
2203             
2204             //return this.items.get(t.menuItemId);
2205         }
2206         
2207         return false;
2208     },
2209     
2210     onTouch : function(e) 
2211     {
2212         Roo.log("menu.onTouch");
2213         //e.stopEvent(); this make the user popdown broken
2214         this.onClick(e);
2215     },
2216     
2217     onClick : function(e)
2218     {
2219         Roo.log("menu.onClick");
2220         
2221         var t = this.findTargetItem(e);
2222         if(!t || t.isContainer){
2223             return;
2224         }
2225         Roo.log(e);
2226         /*
2227         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2228             if(t == this.activeItem && t.shouldDeactivate(e)){
2229                 this.activeItem.deactivate();
2230                 delete this.activeItem;
2231                 return;
2232             }
2233             if(t.canActivate){
2234                 this.setActiveItem(t, true);
2235             }
2236             return;
2237             
2238             
2239         }
2240         */
2241        
2242         Roo.log('pass click event');
2243         
2244         t.onClick(e);
2245         
2246         this.fireEvent("click", this, t, e);
2247         
2248         var _this = this;
2249         
2250         if(!t.href.length || t.href == '#'){
2251             (function() { _this.hide(); }).defer(100);
2252         }
2253         
2254     },
2255     
2256     onMouseOver : function(e){
2257         var t  = this.findTargetItem(e);
2258         //Roo.log(t);
2259         //if(t){
2260         //    if(t.canActivate && !t.disabled){
2261         //        this.setActiveItem(t, true);
2262         //    }
2263         //}
2264         
2265         this.fireEvent("mouseover", this, e, t);
2266     },
2267     isVisible : function(){
2268         return !this.hidden;
2269     },
2270      onMouseOut : function(e){
2271         var t  = this.findTargetItem(e);
2272         
2273         //if(t ){
2274         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2275         //        this.activeItem.deactivate();
2276         //        delete this.activeItem;
2277         //    }
2278         //}
2279         this.fireEvent("mouseout", this, e, t);
2280     },
2281     
2282     
2283     /**
2284      * Displays this menu relative to another element
2285      * @param {String/HTMLElement/Roo.Element} element The element to align to
2286      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287      * the element (defaults to this.defaultAlign)
2288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2289      */
2290     show : function(el, pos, parentMenu){
2291         this.parentMenu = parentMenu;
2292         if(!this.el){
2293             this.render();
2294         }
2295         this.fireEvent("beforeshow", this);
2296         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2297     },
2298      /**
2299      * Displays this menu at a specific xy position
2300      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2302      */
2303     showAt : function(xy, parentMenu, /* private: */_e){
2304         this.parentMenu = parentMenu;
2305         if(!this.el){
2306             this.render();
2307         }
2308         if(_e !== false){
2309             this.fireEvent("beforeshow", this);
2310             //xy = this.el.adjustForConstraints(xy);
2311         }
2312         
2313         //this.el.show();
2314         this.hideMenuItems();
2315         this.hidden = false;
2316         this.triggerEl.addClass('open');
2317         this.el.addClass('show');
2318         
2319         // reassign x when hitting right
2320         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2322         }
2323         
2324         // reassign y when hitting bottom
2325         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2327         }
2328         
2329         // but the list may align on trigger left or trigger top... should it be a properity?
2330         
2331         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2332             this.el.setXY(xy);
2333         }
2334         
2335         this.focus();
2336         this.fireEvent("show", this);
2337     },
2338     
2339     focus : function(){
2340         return;
2341         if(!this.hidden){
2342             this.doFocus.defer(50, this);
2343         }
2344     },
2345
2346     doFocus : function(){
2347         if(!this.hidden){
2348             this.focusEl.focus();
2349         }
2350     },
2351
2352     /**
2353      * Hides this menu and optionally all parent menus
2354      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2355      */
2356     hide : function(deep)
2357     {
2358         
2359         this.hideMenuItems();
2360         if(this.el && this.isVisible()){
2361             this.fireEvent("beforehide", this);
2362             if(this.activeItem){
2363                 this.activeItem.deactivate();
2364                 this.activeItem = null;
2365             }
2366             this.triggerEl.removeClass('open');;
2367             this.el.removeClass('show');
2368             this.hidden = true;
2369             this.fireEvent("hide", this);
2370         }
2371         if(deep === true && this.parentMenu){
2372             this.parentMenu.hide(true);
2373         }
2374     },
2375     
2376     onTriggerClick : function(e)
2377     {
2378         Roo.log('trigger click');
2379         
2380         var target = e.getTarget();
2381         
2382         Roo.log(target.nodeName.toLowerCase());
2383         
2384         if(target.nodeName.toLowerCase() === 'i'){
2385             e.preventDefault();
2386         }
2387         
2388     },
2389     
2390     onTriggerPress  : function(e)
2391     {
2392         Roo.log('trigger press');
2393         //Roo.log(e.getTarget());
2394        // Roo.log(this.triggerEl.dom);
2395        
2396         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397         var pel = Roo.get(e.getTarget());
2398         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399             Roo.log('is treeview or dropdown?');
2400             return;
2401         }
2402         
2403         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2404             return;
2405         }
2406         
2407         if (this.isVisible()) {
2408             Roo.log('hide');
2409             this.hide();
2410         } else {
2411             Roo.log('show');
2412             this.show(this.triggerEl, false, false);
2413         }
2414         
2415         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2416             e.stopEvent();
2417         }
2418         
2419     },
2420        
2421     
2422     hideMenuItems : function()
2423     {
2424         Roo.log("hide Menu Items");
2425         if (!this.el) { 
2426             return;
2427         }
2428         //$(backdrop).remove()
2429         this.el.select('.open',true).each(function(aa) {
2430             
2431             aa.removeClass('open');
2432           //var parent = getParent($(this))
2433           //var relatedTarget = { relatedTarget: this }
2434           
2435            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436           //if (e.isDefaultPrevented()) return
2437            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2438         });
2439     },
2440     addxtypeChild : function (tree, cntr) {
2441         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2442           
2443         this.menuitems.add(comp);
2444         return comp;
2445
2446     },
2447     getEl : function()
2448     {
2449         Roo.log(this.el);
2450         return this.el;
2451     },
2452     
2453     clear : function()
2454     {
2455         this.getEl().dom.innerHTML = '';
2456         this.menuitems.clear();
2457     }
2458 });
2459
2460  
2461  /*
2462  * - LGPL
2463  *
2464  * menu item
2465  * 
2466  */
2467
2468
2469 /**
2470  * @class Roo.bootstrap.MenuItem
2471  * @extends Roo.bootstrap.Component
2472  * Bootstrap MenuItem class
2473  * @cfg {String} html the menu label
2474  * @cfg {String} href the link
2475  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2478  * @cfg {String} fa favicon to show on left of menu item.
2479  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2480  * 
2481  * 
2482  * @constructor
2483  * Create a new MenuItem
2484  * @param {Object} config The config object
2485  */
2486
2487
2488 Roo.bootstrap.MenuItem = function(config){
2489     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2490     this.addEvents({
2491         // raw events
2492         /**
2493          * @event click
2494          * The raw click event for the entire grid.
2495          * @param {Roo.bootstrap.MenuItem} this
2496          * @param {Roo.EventObject} e
2497          */
2498         "click" : true
2499     });
2500 };
2501
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2503     
2504     href : false,
2505     html : false,
2506     preventDefault: false,
2507     isContainer : false,
2508     active : false,
2509     fa: false,
2510     
2511     getAutoCreate : function(){
2512         
2513         if(this.isContainer){
2514             return {
2515                 tag: 'li',
2516                 cls: 'dropdown-menu-item '
2517             };
2518         }
2519         var ctag = {
2520             tag: 'span',
2521             html: 'Link'
2522         };
2523         
2524         var anc = {
2525             tag : 'a',
2526             cls : 'dropdown-item',
2527             href : '#',
2528             cn : [  ]
2529         };
2530         
2531         if (this.fa !== false) {
2532             anc.cn.push({
2533                 tag : 'i',
2534                 cls : 'fa fa-' + this.fa
2535             });
2536         }
2537         
2538         anc.cn.push(ctag);
2539         
2540         
2541         var cfg= {
2542             tag: 'li',
2543             cls: 'dropdown-menu-item',
2544             cn: [ anc ]
2545         };
2546         if (this.parent().type == 'treeview') {
2547             cfg.cls = 'treeview-menu';
2548         }
2549         if (this.active) {
2550             cfg.cls += ' active';
2551         }
2552         
2553         
2554         
2555         anc.href = this.href || cfg.cn[0].href ;
2556         ctag.html = this.html || cfg.cn[0].html ;
2557         return cfg;
2558     },
2559     
2560     initEvents: function()
2561     {
2562         if (this.parent().type == 'treeview') {
2563             this.el.select('a').on('click', this.onClick, this);
2564         }
2565         
2566         if (this.menu) {
2567             this.menu.parentType = this.xtype;
2568             this.menu.triggerEl = this.el;
2569             this.menu = this.addxtype(Roo.apply({}, this.menu));
2570         }
2571         
2572     },
2573     onClick : function(e)
2574     {
2575         Roo.log('item on click ');
2576         
2577         if(this.preventDefault){
2578             e.preventDefault();
2579         }
2580         //this.parent().hideMenuItems();
2581         
2582         this.fireEvent('click', this, e);
2583     },
2584     getEl : function()
2585     {
2586         return this.el;
2587     } 
2588 });
2589
2590  
2591
2592  /*
2593  * - LGPL
2594  *
2595  * menu separator
2596  * 
2597  */
2598
2599
2600 /**
2601  * @class Roo.bootstrap.MenuSeparator
2602  * @extends Roo.bootstrap.Component
2603  * Bootstrap MenuSeparator class
2604  * 
2605  * @constructor
2606  * Create a new MenuItem
2607  * @param {Object} config The config object
2608  */
2609
2610
2611 Roo.bootstrap.MenuSeparator = function(config){
2612     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2613 };
2614
2615 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2616     
2617     getAutoCreate : function(){
2618         var cfg = {
2619             cls: 'divider',
2620             tag : 'li'
2621         };
2622         
2623         return cfg;
2624     }
2625    
2626 });
2627
2628  
2629
2630  
2631 /*
2632 * Licence: LGPL
2633 */
2634
2635 /**
2636  * @class Roo.bootstrap.Modal
2637  * @extends Roo.bootstrap.Component
2638  * Bootstrap Modal class
2639  * @cfg {String} title Title of dialog
2640  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2641  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2642  * @cfg {Boolean} specificTitle default false
2643  * @cfg {Array} buttons Array of buttons or standard button set..
2644  * @cfg {String} buttonPosition (left|right|center) default right
2645  * @cfg {Boolean} animate default true
2646  * @cfg {Boolean} allow_close default true
2647  * @cfg {Boolean} fitwindow default false
2648  * @cfg {String} size (sm|lg) default empty
2649  * @cfg {Number} max_width set the max width of modal
2650  *
2651  *
2652  * @constructor
2653  * Create a new Modal Dialog
2654  * @param {Object} config The config object
2655  */
2656
2657 Roo.bootstrap.Modal = function(config){
2658     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2659     this.addEvents({
2660         // raw events
2661         /**
2662          * @event btnclick
2663          * The raw btnclick event for the button
2664          * @param {Roo.EventObject} e
2665          */
2666         "btnclick" : true,
2667         /**
2668          * @event resize
2669          * Fire when dialog resize
2670          * @param {Roo.bootstrap.Modal} this
2671          * @param {Roo.EventObject} e
2672          */
2673         "resize" : true
2674     });
2675     this.buttons = this.buttons || [];
2676
2677     if (this.tmpl) {
2678         this.tmpl = Roo.factory(this.tmpl);
2679     }
2680
2681 };
2682
2683 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2684
2685     title : 'test dialog',
2686
2687     buttons : false,
2688
2689     // set on load...
2690
2691     html: false,
2692
2693     tmp: false,
2694
2695     specificTitle: false,
2696
2697     buttonPosition: 'right',
2698
2699     allow_close : true,
2700
2701     animate : true,
2702
2703     fitwindow: false,
2704     
2705      // private
2706     dialogEl: false,
2707     bodyEl:  false,
2708     footerEl:  false,
2709     titleEl:  false,
2710     closeEl:  false,
2711
2712     size: '',
2713     
2714     max_width: 0,
2715     
2716     max_height: 0,
2717     
2718     fit_content: false,
2719
2720     onRender : function(ct, position)
2721     {
2722         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2723
2724         if(!this.el){
2725             var cfg = Roo.apply({},  this.getAutoCreate());
2726             cfg.id = Roo.id();
2727             //if(!cfg.name){
2728             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2729             //}
2730             //if (!cfg.name.length) {
2731             //    delete cfg.name;
2732            // }
2733             if (this.cls) {
2734                 cfg.cls += ' ' + this.cls;
2735             }
2736             if (this.style) {
2737                 cfg.style = this.style;
2738             }
2739             this.el = Roo.get(document.body).createChild(cfg, position);
2740         }
2741         //var type = this.el.dom.type;
2742
2743
2744         if(this.tabIndex !== undefined){
2745             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2746         }
2747
2748         this.dialogEl = this.el.select('.modal-dialog',true).first();
2749         this.bodyEl = this.el.select('.modal-body',true).first();
2750         this.closeEl = this.el.select('.modal-header .close', true).first();
2751         this.headerEl = this.el.select('.modal-header',true).first();
2752         this.titleEl = this.el.select('.modal-title',true).first();
2753         this.footerEl = this.el.select('.modal-footer',true).first();
2754
2755         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2756         
2757         //this.el.addClass("x-dlg-modal");
2758
2759         if (this.buttons.length) {
2760             Roo.each(this.buttons, function(bb) {
2761                 var b = Roo.apply({}, bb);
2762                 b.xns = b.xns || Roo.bootstrap;
2763                 b.xtype = b.xtype || 'Button';
2764                 if (typeof(b.listeners) == 'undefined') {
2765                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2766                 }
2767
2768                 var btn = Roo.factory(b);
2769
2770                 btn.render(this.el.select('.modal-footer div').first());
2771
2772             },this);
2773         }
2774         // render the children.
2775         var nitems = [];
2776
2777         if(typeof(this.items) != 'undefined'){
2778             var items = this.items;
2779             delete this.items;
2780
2781             for(var i =0;i < items.length;i++) {
2782                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2783             }
2784         }
2785
2786         this.items = nitems;
2787
2788         // where are these used - they used to be body/close/footer
2789
2790
2791         this.initEvents();
2792         //this.el.addClass([this.fieldClass, this.cls]);
2793
2794     },
2795
2796     getAutoCreate : function()
2797     {
2798         var bdy = {
2799                 cls : 'modal-body',
2800                 html : this.html || ''
2801         };
2802
2803         var title = {
2804             tag: 'h4',
2805             cls : 'modal-title',
2806             html : this.title
2807         };
2808
2809         if(this.specificTitle){
2810             title = this.title;
2811
2812         };
2813
2814         var header = [];
2815         if (this.allow_close && Roo.bootstrap.version == 3) {
2816             header.push({
2817                 tag: 'button',
2818                 cls : 'close',
2819                 html : '&times'
2820             });
2821         }
2822
2823         header.push(title);
2824
2825         if (this.allow_close && Roo.bootstrap.version == 4) {
2826             header.push({
2827                 tag: 'button',
2828                 cls : 'close',
2829                 html : '&times'
2830             });
2831         }
2832         
2833         var size = '';
2834
2835         if(this.size.length){
2836             size = 'modal-' + this.size;
2837         }
2838
2839         var modal = {
2840             cls: "modal",
2841              cn : [
2842                 {
2843                     cls: "modal-dialog " + size,
2844                     cn : [
2845                         {
2846                             cls : "modal-content",
2847                             cn : [
2848                                 {
2849                                     cls : 'modal-header',
2850                                     cn : header
2851                                 },
2852                                 bdy,
2853                                 {
2854                                     cls : 'modal-footer',
2855                                     cn : [
2856                                         {
2857                                             tag: 'div',
2858                                             cls: 'btn-' + this.buttonPosition
2859                                         }
2860                                     ]
2861
2862                                 }
2863
2864
2865                             ]
2866
2867                         }
2868                     ]
2869
2870                 }
2871             ]
2872         };
2873
2874         if(this.animate){
2875             modal.cls += ' fade';
2876         }
2877
2878         return modal;
2879
2880     },
2881     getChildContainer : function() {
2882
2883          return this.bodyEl;
2884
2885     },
2886     getButtonContainer : function() {
2887          return this.el.select('.modal-footer div',true).first();
2888
2889     },
2890     initEvents : function()
2891     {
2892         if (this.allow_close) {
2893             this.closeEl.on('click', this.hide, this);
2894         }
2895         Roo.EventManager.onWindowResize(this.resize, this, true);
2896
2897
2898     },
2899
2900     resize : function()
2901     {
2902         this.maskEl.setSize(
2903             Roo.lib.Dom.getViewWidth(true),
2904             Roo.lib.Dom.getViewHeight(true)
2905         );
2906         
2907         if (this.fitwindow) {
2908             this.setSize(
2909                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2910                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2911             );
2912             return;
2913         }
2914         
2915         if(this.max_width !== 0) {
2916             
2917             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2918             
2919             if(this.height) {
2920                 this.setSize(w, this.height);
2921                 return;
2922             }
2923             
2924             if(this.max_height) {
2925                 this.setSize(w,Math.min(
2926                     this.max_height,
2927                     Roo.lib.Dom.getViewportHeight(true) - 60
2928                 ));
2929                 
2930                 return;
2931             }
2932             
2933             if(!this.fit_content) {
2934                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2935                 return;
2936             }
2937             
2938             this.setSize(w, Math.min(
2939                 60 +
2940                 this.headerEl.getHeight() + 
2941                 this.footerEl.getHeight() + 
2942                 this.getChildHeight(this.bodyEl.dom.childNodes),
2943                 Roo.lib.Dom.getViewportHeight(true) - 60)
2944             );
2945         }
2946         
2947     },
2948
2949     setSize : function(w,h)
2950     {
2951         if (!w && !h) {
2952             return;
2953         }
2954         
2955         this.resizeTo(w,h);
2956     },
2957
2958     show : function() {
2959
2960         if (!this.rendered) {
2961             this.render();
2962         }
2963
2964         //this.el.setStyle('display', 'block');
2965         this.el.removeClass('hideing');
2966         this.el.dom.style.display='block';
2967         
2968         Roo.get(document.body).addClass('modal-open');
2969  
2970         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2971             var _this = this;
2972             (function(){
2973                 this.el.addClass('show');
2974                 this.el.addClass('in');
2975             }).defer(50, this);
2976         }else{
2977             this.el.addClass('show');
2978             this.el.addClass('in');
2979         }
2980
2981         // not sure how we can show data in here..
2982         //if (this.tmpl) {
2983         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2984         //}
2985
2986         Roo.get(document.body).addClass("x-body-masked");
2987         
2988         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2989         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2990         this.maskEl.dom.style.display = 'block';
2991         this.maskEl.addClass('show');
2992         
2993         
2994         this.resize();
2995         
2996         this.fireEvent('show', this);
2997
2998         // set zindex here - otherwise it appears to be ignored...
2999         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3000
3001         (function () {
3002             this.items.forEach( function(e) {
3003                 e.layout ? e.layout() : false;
3004
3005             });
3006         }).defer(100,this);
3007
3008     },
3009     hide : function()
3010     {
3011         if(this.fireEvent("beforehide", this) !== false){
3012             
3013             this.maskEl.removeClass('show');
3014             
3015             this.maskEl.dom.style.display = '';
3016             Roo.get(document.body).removeClass("x-body-masked");
3017             this.el.removeClass('in');
3018             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3019
3020             if(this.animate){ // why
3021                 this.el.addClass('hideing');
3022                 this.el.removeClass('show');
3023                 (function(){
3024                     if (!this.el.hasClass('hideing')) {
3025                         return; // it's been shown again...
3026                     }
3027                     
3028                     this.el.dom.style.display='';
3029
3030                     Roo.get(document.body).removeClass('modal-open');
3031                     this.el.removeClass('hideing');
3032                 }).defer(150,this);
3033                 
3034             }else{
3035                 this.el.removeClass('show');
3036                 this.el.dom.style.display='';
3037                 Roo.get(document.body).removeClass('modal-open');
3038
3039             }
3040             this.fireEvent('hide', this);
3041         }
3042     },
3043     isVisible : function()
3044     {
3045         
3046         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3047         
3048     },
3049
3050     addButton : function(str, cb)
3051     {
3052
3053
3054         var b = Roo.apply({}, { html : str } );
3055         b.xns = b.xns || Roo.bootstrap;
3056         b.xtype = b.xtype || 'Button';
3057         if (typeof(b.listeners) == 'undefined') {
3058             b.listeners = { click : cb.createDelegate(this)  };
3059         }
3060
3061         var btn = Roo.factory(b);
3062
3063         btn.render(this.el.select('.modal-footer div').first());
3064
3065         return btn;
3066
3067     },
3068
3069     setDefaultButton : function(btn)
3070     {
3071         //this.el.select('.modal-footer').()
3072     },
3073     diff : false,
3074
3075     resizeTo: function(w,h)
3076     {
3077         // skip.. ?? why??
3078
3079         this.dialogEl.setWidth(w);
3080         if (this.diff === false) {
3081             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3082         }
3083
3084         this.bodyEl.setHeight(h - this.diff);
3085
3086         this.fireEvent('resize', this);
3087
3088     },
3089     setContentSize  : function(w, h)
3090     {
3091
3092     },
3093     onButtonClick: function(btn,e)
3094     {
3095         //Roo.log([a,b,c]);
3096         this.fireEvent('btnclick', btn.name, e);
3097     },
3098      /**
3099      * Set the title of the Dialog
3100      * @param {String} str new Title
3101      */
3102     setTitle: function(str) {
3103         this.titleEl.dom.innerHTML = str;
3104     },
3105     /**
3106      * Set the body of the Dialog
3107      * @param {String} str new Title
3108      */
3109     setBody: function(str) {
3110         this.bodyEl.dom.innerHTML = str;
3111     },
3112     /**
3113      * Set the body of the Dialog using the template
3114      * @param {Obj} data - apply this data to the template and replace the body contents.
3115      */
3116     applyBody: function(obj)
3117     {
3118         if (!this.tmpl) {
3119             Roo.log("Error - using apply Body without a template");
3120             //code
3121         }
3122         this.tmpl.overwrite(this.bodyEl, obj);
3123     },
3124     
3125     getChildHeight : function(child_nodes)
3126     {
3127         if(
3128             !child_nodes ||
3129             child_nodes.length == 0
3130         ) {
3131             return;
3132         }
3133         
3134         var child_height = 0;
3135         
3136         for(var i = 0; i < child_nodes.length; i++) {
3137             
3138             /*
3139             * for modal with tabs...
3140             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3141                 
3142                 var layout_childs = child_nodes[i].childNodes;
3143                 
3144                 for(var j = 0; j < layout_childs.length; j++) {
3145                     
3146                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3147                         
3148                         var layout_body_childs = layout_childs[j].childNodes;
3149                         
3150                         for(var k = 0; k < layout_body_childs.length; k++) {
3151                             
3152                             if(layout_body_childs[k].classList.contains('navbar')) {
3153                                 child_height += layout_body_childs[k].offsetHeight;
3154                                 continue;
3155                             }
3156                             
3157                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3158                                 
3159                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3160                                 
3161                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3162                                     
3163                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3164                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3165                                         continue;
3166                                     }
3167                                     
3168                                 }
3169                                 
3170                             }
3171                             
3172                         }
3173                     }
3174                 }
3175                 continue;
3176             }
3177             */
3178             
3179             child_height += child_nodes[i].offsetHeight;
3180             // Roo.log(child_nodes[i].offsetHeight);
3181         }
3182         
3183         return child_height;
3184     }
3185
3186 });
3187
3188
3189 Roo.apply(Roo.bootstrap.Modal,  {
3190     /**
3191          * Button config that displays a single OK button
3192          * @type Object
3193          */
3194         OK :  [{
3195             name : 'ok',
3196             weight : 'primary',
3197             html : 'OK'
3198         }],
3199         /**
3200          * Button config that displays Yes and No buttons
3201          * @type Object
3202          */
3203         YESNO : [
3204             {
3205                 name  : 'no',
3206                 html : 'No'
3207             },
3208             {
3209                 name  :'yes',
3210                 weight : 'primary',
3211                 html : 'Yes'
3212             }
3213         ],
3214
3215         /**
3216          * Button config that displays OK and Cancel buttons
3217          * @type Object
3218          */
3219         OKCANCEL : [
3220             {
3221                name : 'cancel',
3222                 html : 'Cancel'
3223             },
3224             {
3225                 name : 'ok',
3226                 weight : 'primary',
3227                 html : 'OK'
3228             }
3229         ],
3230         /**
3231          * Button config that displays Yes, No and Cancel buttons
3232          * @type Object
3233          */
3234         YESNOCANCEL : [
3235             {
3236                 name : 'yes',
3237                 weight : 'primary',
3238                 html : 'Yes'
3239             },
3240             {
3241                 name : 'no',
3242                 html : 'No'
3243             },
3244             {
3245                 name : 'cancel',
3246                 html : 'Cancel'
3247             }
3248         ],
3249         
3250         zIndex : 10001
3251 });
3252 /*
3253  * - LGPL
3254  *
3255  * messagebox - can be used as a replace
3256  * 
3257  */
3258 /**
3259  * @class Roo.MessageBox
3260  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3261  * Example usage:
3262  *<pre><code>
3263 // Basic alert:
3264 Roo.Msg.alert('Status', 'Changes saved successfully.');
3265
3266 // Prompt for user data:
3267 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3268     if (btn == 'ok'){
3269         // process text value...
3270     }
3271 });
3272
3273 // Show a dialog using config options:
3274 Roo.Msg.show({
3275    title:'Save Changes?',
3276    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3277    buttons: Roo.Msg.YESNOCANCEL,
3278    fn: processResult,
3279    animEl: 'elId'
3280 });
3281 </code></pre>
3282  * @singleton
3283  */
3284 Roo.bootstrap.MessageBox = function(){
3285     var dlg, opt, mask, waitTimer;
3286     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3287     var buttons, activeTextEl, bwidth;
3288
3289     
3290     // private
3291     var handleButton = function(button){
3292         dlg.hide();
3293         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3294     };
3295
3296     // private
3297     var handleHide = function(){
3298         if(opt && opt.cls){
3299             dlg.el.removeClass(opt.cls);
3300         }
3301         //if(waitTimer){
3302         //    Roo.TaskMgr.stop(waitTimer);
3303         //    waitTimer = null;
3304         //}
3305     };
3306
3307     // private
3308     var updateButtons = function(b){
3309         var width = 0;
3310         if(!b){
3311             buttons["ok"].hide();
3312             buttons["cancel"].hide();
3313             buttons["yes"].hide();
3314             buttons["no"].hide();
3315             //dlg.footer.dom.style.display = 'none';
3316             return width;
3317         }
3318         dlg.footerEl.dom.style.display = '';
3319         for(var k in buttons){
3320             if(typeof buttons[k] != "function"){
3321                 if(b[k]){
3322                     buttons[k].show();
3323                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3324                     width += buttons[k].el.getWidth()+15;
3325                 }else{
3326                     buttons[k].hide();
3327                 }
3328             }
3329         }
3330         return width;
3331     };
3332
3333     // private
3334     var handleEsc = function(d, k, e){
3335         if(opt && opt.closable !== false){
3336             dlg.hide();
3337         }
3338         if(e){
3339             e.stopEvent();
3340         }
3341     };
3342
3343     return {
3344         /**
3345          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3346          * @return {Roo.BasicDialog} The BasicDialog element
3347          */
3348         getDialog : function(){
3349            if(!dlg){
3350                 dlg = new Roo.bootstrap.Modal( {
3351                     //draggable: true,
3352                     //resizable:false,
3353                     //constraintoviewport:false,
3354                     //fixedcenter:true,
3355                     //collapsible : false,
3356                     //shim:true,
3357                     //modal: true,
3358                 //    width: 'auto',
3359                   //  height:100,
3360                     //buttonAlign:"center",
3361                     closeClick : function(){
3362                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3363                             handleButton("no");
3364                         }else{
3365                             handleButton("cancel");
3366                         }
3367                     }
3368                 });
3369                 dlg.render();
3370                 dlg.on("hide", handleHide);
3371                 mask = dlg.mask;
3372                 //dlg.addKeyListener(27, handleEsc);
3373                 buttons = {};
3374                 this.buttons = buttons;
3375                 var bt = this.buttonText;
3376                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3377                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3378                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3379                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3380                 //Roo.log(buttons);
3381                 bodyEl = dlg.bodyEl.createChild({
3382
3383                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3384                         '<textarea class="roo-mb-textarea"></textarea>' +
3385                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3386                 });
3387                 msgEl = bodyEl.dom.firstChild;
3388                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3389                 textboxEl.enableDisplayMode();
3390                 textboxEl.addKeyListener([10,13], function(){
3391                     if(dlg.isVisible() && opt && opt.buttons){
3392                         if(opt.buttons.ok){
3393                             handleButton("ok");
3394                         }else if(opt.buttons.yes){
3395                             handleButton("yes");
3396                         }
3397                     }
3398                 });
3399                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3400                 textareaEl.enableDisplayMode();
3401                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3402                 progressEl.enableDisplayMode();
3403                 
3404                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3405                 var pf = progressEl.dom.firstChild;
3406                 if (pf) {
3407                     pp = Roo.get(pf.firstChild);
3408                     pp.setHeight(pf.offsetHeight);
3409                 }
3410                 
3411             }
3412             return dlg;
3413         },
3414
3415         /**
3416          * Updates the message box body text
3417          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3418          * the XHTML-compliant non-breaking space character '&amp;#160;')
3419          * @return {Roo.MessageBox} This message box
3420          */
3421         updateText : function(text)
3422         {
3423             if(!dlg.isVisible() && !opt.width){
3424                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3425                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3426             }
3427             msgEl.innerHTML = text || '&#160;';
3428       
3429             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3430             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3431             var w = Math.max(
3432                     Math.min(opt.width || cw , this.maxWidth), 
3433                     Math.max(opt.minWidth || this.minWidth, bwidth)
3434             );
3435             if(opt.prompt){
3436                 activeTextEl.setWidth(w);
3437             }
3438             if(dlg.isVisible()){
3439                 dlg.fixedcenter = false;
3440             }
3441             // to big, make it scroll. = But as usual stupid IE does not support
3442             // !important..
3443             
3444             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3445                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3446                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3447             } else {
3448                 bodyEl.dom.style.height = '';
3449                 bodyEl.dom.style.overflowY = '';
3450             }
3451             if (cw > w) {
3452                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3453             } else {
3454                 bodyEl.dom.style.overflowX = '';
3455             }
3456             
3457             dlg.setContentSize(w, bodyEl.getHeight());
3458             if(dlg.isVisible()){
3459                 dlg.fixedcenter = true;
3460             }
3461             return this;
3462         },
3463
3464         /**
3465          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3466          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3467          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3468          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3469          * @return {Roo.MessageBox} This message box
3470          */
3471         updateProgress : function(value, text){
3472             if(text){
3473                 this.updateText(text);
3474             }
3475             
3476             if (pp) { // weird bug on my firefox - for some reason this is not defined
3477                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3478                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3479             }
3480             return this;
3481         },        
3482
3483         /**
3484          * Returns true if the message box is currently displayed
3485          * @return {Boolean} True if the message box is visible, else false
3486          */
3487         isVisible : function(){
3488             return dlg && dlg.isVisible();  
3489         },
3490
3491         /**
3492          * Hides the message box if it is displayed
3493          */
3494         hide : function(){
3495             if(this.isVisible()){
3496                 dlg.hide();
3497             }  
3498         },
3499
3500         /**
3501          * Displays a new message box, or reinitializes an existing message box, based on the config options
3502          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3503          * The following config object properties are supported:
3504          * <pre>
3505 Property    Type             Description
3506 ----------  ---------------  ------------------------------------------------------------------------------------
3507 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3508                                    closes (defaults to undefined)
3509 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3510                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3511 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3512                                    progress and wait dialogs will ignore this property and always hide the
3513                                    close button as they can only be closed programmatically.
3514 cls               String           A custom CSS class to apply to the message box element
3515 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3516                                    displayed (defaults to 75)
3517 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3518                                    function will be btn (the name of the button that was clicked, if applicable,
3519                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3520                                    Progress and wait dialogs will ignore this option since they do not respond to
3521                                    user actions and can only be closed programmatically, so any required function
3522                                    should be called by the same code after it closes the dialog.
3523 icon              String           A CSS class that provides a background image to be used as an icon for
3524                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3525 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3526 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3527 modal             Boolean          False to allow user interaction with the page while the message box is
3528                                    displayed (defaults to true)
3529 msg               String           A string that will replace the existing message box body text (defaults
3530                                    to the XHTML-compliant non-breaking space character '&#160;')
3531 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3532 progress          Boolean          True to display a progress bar (defaults to false)
3533 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3534 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3535 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3536 title             String           The title text
3537 value             String           The string value to set into the active textbox element if displayed
3538 wait              Boolean          True to display a progress bar (defaults to false)
3539 width             Number           The width of the dialog in pixels
3540 </pre>
3541          *
3542          * Example usage:
3543          * <pre><code>
3544 Roo.Msg.show({
3545    title: 'Address',
3546    msg: 'Please enter your address:',
3547    width: 300,
3548    buttons: Roo.MessageBox.OKCANCEL,
3549    multiline: true,
3550    fn: saveAddress,
3551    animEl: 'addAddressBtn'
3552 });
3553 </code></pre>
3554          * @param {Object} config Configuration options
3555          * @return {Roo.MessageBox} This message box
3556          */
3557         show : function(options)
3558         {
3559             
3560             // this causes nightmares if you show one dialog after another
3561             // especially on callbacks..
3562              
3563             if(this.isVisible()){
3564                 
3565                 this.hide();
3566                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3567                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3568                 Roo.log("New Dialog Message:" +  options.msg )
3569                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3570                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3571                 
3572             }
3573             var d = this.getDialog();
3574             opt = options;
3575             d.setTitle(opt.title || "&#160;");
3576             d.closeEl.setDisplayed(opt.closable !== false);
3577             activeTextEl = textboxEl;
3578             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3579             if(opt.prompt){
3580                 if(opt.multiline){
3581                     textboxEl.hide();
3582                     textareaEl.show();
3583                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3584                         opt.multiline : this.defaultTextHeight);
3585                     activeTextEl = textareaEl;
3586                 }else{
3587                     textboxEl.show();
3588                     textareaEl.hide();
3589                 }
3590             }else{
3591                 textboxEl.hide();
3592                 textareaEl.hide();
3593             }
3594             progressEl.setDisplayed(opt.progress === true);
3595             this.updateProgress(0);
3596             activeTextEl.dom.value = opt.value || "";
3597             if(opt.prompt){
3598                 dlg.setDefaultButton(activeTextEl);
3599             }else{
3600                 var bs = opt.buttons;
3601                 var db = null;
3602                 if(bs && bs.ok){
3603                     db = buttons["ok"];
3604                 }else if(bs && bs.yes){
3605                     db = buttons["yes"];
3606                 }
3607                 dlg.setDefaultButton(db);
3608             }
3609             bwidth = updateButtons(opt.buttons);
3610             this.updateText(opt.msg);
3611             if(opt.cls){
3612                 d.el.addClass(opt.cls);
3613             }
3614             d.proxyDrag = opt.proxyDrag === true;
3615             d.modal = opt.modal !== false;
3616             d.mask = opt.modal !== false ? mask : false;
3617             if(!d.isVisible()){
3618                 // force it to the end of the z-index stack so it gets a cursor in FF
3619                 document.body.appendChild(dlg.el.dom);
3620                 d.animateTarget = null;
3621                 d.show(options.animEl);
3622             }
3623             return this;
3624         },
3625
3626         /**
3627          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3628          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3629          * and closing the message box when the process is complete.
3630          * @param {String} title The title bar text
3631          * @param {String} msg The message box body text
3632          * @return {Roo.MessageBox} This message box
3633          */
3634         progress : function(title, msg){
3635             this.show({
3636                 title : title,
3637                 msg : msg,
3638                 buttons: false,
3639                 progress:true,
3640                 closable:false,
3641                 minWidth: this.minProgressWidth,
3642                 modal : true
3643             });
3644             return this;
3645         },
3646
3647         /**
3648          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3649          * If a callback function is passed it will be called after the user clicks the button, and the
3650          * id of the button that was clicked will be passed as the only parameter to the callback
3651          * (could also be the top-right close button).
3652          * @param {String} title The title bar text
3653          * @param {String} msg The message box body text
3654          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3655          * @param {Object} scope (optional) The scope of the callback function
3656          * @return {Roo.MessageBox} This message box
3657          */
3658         alert : function(title, msg, fn, scope)
3659         {
3660             this.show({
3661                 title : title,
3662                 msg : msg,
3663                 buttons: this.OK,
3664                 fn: fn,
3665                 closable : false,
3666                 scope : scope,
3667                 modal : true
3668             });
3669             return this;
3670         },
3671
3672         /**
3673          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3674          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3675          * You are responsible for closing the message box when the process is complete.
3676          * @param {String} msg The message box body text
3677          * @param {String} title (optional) The title bar text
3678          * @return {Roo.MessageBox} This message box
3679          */
3680         wait : function(msg, title){
3681             this.show({
3682                 title : title,
3683                 msg : msg,
3684                 buttons: false,
3685                 closable:false,
3686                 progress:true,
3687                 modal:true,
3688                 width:300,
3689                 wait:true
3690             });
3691             waitTimer = Roo.TaskMgr.start({
3692                 run: function(i){
3693                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3694                 },
3695                 interval: 1000
3696             });
3697             return this;
3698         },
3699
3700         /**
3701          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3702          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3703          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3704          * @param {String} title The title bar text
3705          * @param {String} msg The message box body text
3706          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3707          * @param {Object} scope (optional) The scope of the callback function
3708          * @return {Roo.MessageBox} This message box
3709          */
3710         confirm : function(title, msg, fn, scope){
3711             this.show({
3712                 title : title,
3713                 msg : msg,
3714                 buttons: this.YESNO,
3715                 fn: fn,
3716                 scope : scope,
3717                 modal : true
3718             });
3719             return this;
3720         },
3721
3722         /**
3723          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3724          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3725          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3726          * (could also be the top-right close button) and the text that was entered will be passed as the two
3727          * parameters to the callback.
3728          * @param {String} title The title bar text
3729          * @param {String} msg The message box body text
3730          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3731          * @param {Object} scope (optional) The scope of the callback function
3732          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3733          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3734          * @return {Roo.MessageBox} This message box
3735          */
3736         prompt : function(title, msg, fn, scope, multiline){
3737             this.show({
3738                 title : title,
3739                 msg : msg,
3740                 buttons: this.OKCANCEL,
3741                 fn: fn,
3742                 minWidth:250,
3743                 scope : scope,
3744                 prompt:true,
3745                 multiline: multiline,
3746                 modal : true
3747             });
3748             return this;
3749         },
3750
3751         /**
3752          * Button config that displays a single OK button
3753          * @type Object
3754          */
3755         OK : {ok:true},
3756         /**
3757          * Button config that displays Yes and No buttons
3758          * @type Object
3759          */
3760         YESNO : {yes:true, no:true},
3761         /**
3762          * Button config that displays OK and Cancel buttons
3763          * @type Object
3764          */
3765         OKCANCEL : {ok:true, cancel:true},
3766         /**
3767          * Button config that displays Yes, No and Cancel buttons
3768          * @type Object
3769          */
3770         YESNOCANCEL : {yes:true, no:true, cancel:true},
3771
3772         /**
3773          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3774          * @type Number
3775          */
3776         defaultTextHeight : 75,
3777         /**
3778          * The maximum width in pixels of the message box (defaults to 600)
3779          * @type Number
3780          */
3781         maxWidth : 600,
3782         /**
3783          * The minimum width in pixels of the message box (defaults to 100)
3784          * @type Number
3785          */
3786         minWidth : 100,
3787         /**
3788          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3789          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3790          * @type Number
3791          */
3792         minProgressWidth : 250,
3793         /**
3794          * An object containing the default button text strings that can be overriden for localized language support.
3795          * Supported properties are: ok, cancel, yes and no.
3796          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3797          * @type Object
3798          */
3799         buttonText : {
3800             ok : "OK",
3801             cancel : "Cancel",
3802             yes : "Yes",
3803             no : "No"
3804         }
3805     };
3806 }();
3807
3808 /**
3809  * Shorthand for {@link Roo.MessageBox}
3810  */
3811 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3812 Roo.Msg = Roo.Msg || Roo.MessageBox;
3813 /*
3814  * - LGPL
3815  *
3816  * navbar
3817  * 
3818  */
3819
3820 /**
3821  * @class Roo.bootstrap.Navbar
3822  * @extends Roo.bootstrap.Component
3823  * Bootstrap Navbar class
3824
3825  * @constructor
3826  * Create a new Navbar
3827  * @param {Object} config The config object
3828  */
3829
3830
3831 Roo.bootstrap.Navbar = function(config){
3832     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3833     this.addEvents({
3834         // raw events
3835         /**
3836          * @event beforetoggle
3837          * Fire before toggle the menu
3838          * @param {Roo.EventObject} e
3839          */
3840         "beforetoggle" : true
3841     });
3842 };
3843
3844 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3845     
3846     
3847    
3848     // private
3849     navItems : false,
3850     loadMask : false,
3851     
3852     
3853     getAutoCreate : function(){
3854         
3855         
3856         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3857         
3858     },
3859     
3860     initEvents :function ()
3861     {
3862         //Roo.log(this.el.select('.navbar-toggle',true));
3863         this.el.select('.navbar-toggle',true).on('click', function() {
3864             if(this.fireEvent('beforetoggle', this) !== false){
3865                 var ce = this.el.select('.navbar-collapse',true).first();
3866                 ce.toggleClass('in'); // old...
3867                 if (ce.hasClass('collapse')) {
3868                     // show it...
3869                     ce.removeClass('collapse');
3870                     ce.addClass('show');
3871                     var h = ce.getHeight();
3872                     Roo.log(h);
3873                     ce.removeClass('show');
3874                     // at this point we should be able to see it..
3875                     ce.addClass('collapsing');
3876                     
3877                     ce.setHeight(0); // resize it ...
3878                     ce.on('transitionend', function() {
3879                         Roo.log('done transition');
3880                         ce.removeClass('collapsing');
3881                         ce.addClass('show');
3882                         ce.removeClass('collapse');
3883
3884                         ce.dom.style.height = '';
3885                     }, this, { single: true} );
3886                     ce.setHeight(h);
3887                     
3888                 } else {
3889                     ce.setHeight(ce.getHeight());
3890                     ce.removeClass('show');
3891                     ce.addClass('collapsing');
3892                     
3893                     ce.on('transitionend', function() {
3894                         ce.dom.style.height = '';
3895                         ce.removeClass('collapsing');
3896                         ce.addClass('collapse');
3897                     }, this, { single: true} );
3898                     ce.setHeight(0);
3899                 }
3900             }
3901             
3902         }, this);
3903         
3904         var mark = {
3905             tag: "div",
3906             cls:"x-dlg-mask"
3907         };
3908         
3909         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3910         
3911         var size = this.el.getSize();
3912         this.maskEl.setSize(size.width, size.height);
3913         this.maskEl.enableDisplayMode("block");
3914         this.maskEl.hide();
3915         
3916         if(this.loadMask){
3917             this.maskEl.show();
3918         }
3919     },
3920     
3921     
3922     getChildContainer : function()
3923     {
3924         if (this.el.select('.collapse').getCount()) {
3925             return this.el.select('.collapse',true).first();
3926         }
3927         
3928         return this.el;
3929     },
3930     
3931     mask : function()
3932     {
3933         this.maskEl.show();
3934     },
3935     
3936     unmask : function()
3937     {
3938         this.maskEl.hide();
3939     } 
3940     
3941     
3942     
3943     
3944 });
3945
3946
3947
3948  
3949
3950  /*
3951  * - LGPL
3952  *
3953  * navbar
3954  * 
3955  */
3956
3957 /**
3958  * @class Roo.bootstrap.NavSimplebar
3959  * @extends Roo.bootstrap.Navbar
3960  * Bootstrap Sidebar class
3961  *
3962  * @cfg {Boolean} inverse is inverted color
3963  * 
3964  * @cfg {String} type (nav | pills | tabs)
3965  * @cfg {Boolean} arrangement stacked | justified
3966  * @cfg {String} align (left | right) alignment
3967  * 
3968  * @cfg {Boolean} main (true|false) main nav bar? default false
3969  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3970  * 
3971  * @cfg {String} tag (header|footer|nav|div) default is nav 
3972
3973  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3974  * 
3975  * 
3976  * @constructor
3977  * Create a new Sidebar
3978  * @param {Object} config The config object
3979  */
3980
3981
3982 Roo.bootstrap.NavSimplebar = function(config){
3983     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3984 };
3985
3986 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3987     
3988     inverse: false,
3989     
3990     type: false,
3991     arrangement: '',
3992     align : false,
3993     
3994     weight : 'light',
3995     
3996     main : false,
3997     
3998     
3999     tag : false,
4000     
4001     
4002     getAutoCreate : function(){
4003         
4004         
4005         var cfg = {
4006             tag : this.tag || 'div',
4007             cls : 'navbar navbar-expand-lg'
4008         };
4009         if (['light','white'].indexOf(this.weight) > -1) {
4010             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4011         }
4012         cfg.cls += ' bg-' + this.weight;
4013         
4014           
4015         
4016         cfg.cn = [
4017             {
4018                 cls: 'nav',
4019                 tag : 'ul'
4020             }
4021         ];
4022         
4023          
4024         this.type = this.type || 'nav';
4025         if (['tabs','pills'].indexOf(this.type)!==-1) {
4026             cfg.cn[0].cls += ' nav-' + this.type
4027         
4028         
4029         } else {
4030             if (this.type!=='nav') {
4031                 Roo.log('nav type must be nav/tabs/pills')
4032             }
4033             cfg.cn[0].cls += ' navbar-nav'
4034         }
4035         
4036         
4037         
4038         
4039         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4040             cfg.cn[0].cls += ' nav-' + this.arrangement;
4041         }
4042         
4043         
4044         if (this.align === 'right') {
4045             cfg.cn[0].cls += ' navbar-right';
4046         }
4047         
4048         if (this.inverse) {
4049             cfg.cls += ' navbar-inverse';
4050             
4051         }
4052         
4053         
4054         return cfg;
4055     
4056         
4057     }
4058     
4059     
4060     
4061 });
4062
4063
4064
4065  
4066
4067  
4068        /*
4069  * - LGPL
4070  *
4071  * navbar
4072  * navbar-fixed-top
4073  * navbar-expand-md  fixed-top 
4074  */
4075
4076 /**
4077  * @class Roo.bootstrap.NavHeaderbar
4078  * @extends Roo.bootstrap.NavSimplebar
4079  * Bootstrap Sidebar class
4080  *
4081  * @cfg {String} brand what is brand
4082  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4083  * @cfg {String} brand_href href of the brand
4084  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4085  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4086  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4087  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4088  * 
4089  * @constructor
4090  * Create a new Sidebar
4091  * @param {Object} config The config object
4092  */
4093
4094
4095 Roo.bootstrap.NavHeaderbar = function(config){
4096     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4097       
4098 };
4099
4100 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4101     
4102     position: '',
4103     brand: '',
4104     brand_href: false,
4105     srButton : true,
4106     autohide : false,
4107     desktopCenter : false,
4108    
4109     
4110     getAutoCreate : function(){
4111         
4112         var   cfg = {
4113             tag: this.nav || 'nav',
4114             cls: 'navbar navbar-expand-md',
4115             role: 'navigation',
4116             cn: []
4117         };
4118         
4119         var cn = cfg.cn;
4120         if (this.desktopCenter) {
4121             cn.push({cls : 'container', cn : []});
4122             cn = cn[0].cn;
4123         }
4124         
4125         if(this.srButton){
4126             var btn = {
4127                 tag: 'button',
4128                 type: 'button',
4129                 cls: 'navbar-toggle navbar-toggler',
4130                 'data-toggle': 'collapse',
4131                 cn: [
4132                     {
4133                         tag: 'span',
4134                         cls: 'sr-only',
4135                         html: 'Toggle navigation'
4136                     },
4137                     {
4138                         tag: 'span',
4139                         cls: 'icon-bar navbar-toggler-icon'
4140                     },
4141                     {
4142                         tag: 'span',
4143                         cls: 'icon-bar'
4144                     },
4145                     {
4146                         tag: 'span',
4147                         cls: 'icon-bar'
4148                     }
4149                 ]
4150             };
4151             
4152             cn.push( Roo.bootstrap.version == 4 ? btn : {
4153                 tag: 'div',
4154                 cls: 'navbar-header',
4155                 cn: [
4156                     btn
4157                 ]
4158             });
4159         }
4160         
4161         cn.push({
4162             tag: 'div',
4163             cls: 'collapse navbar-collapse',
4164             cn : []
4165         });
4166         
4167         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4168         
4169         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4170             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4171             
4172             // tag can override this..
4173             
4174             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4175         }
4176         
4177         if (this.brand !== '') {
4178             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4179             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4180                 tag: 'a',
4181                 href: this.brand_href ? this.brand_href : '#',
4182                 cls: 'navbar-brand',
4183                 cn: [
4184                 this.brand
4185                 ]
4186             });
4187         }
4188         
4189         if(this.main){
4190             cfg.cls += ' main-nav';
4191         }
4192         
4193         
4194         return cfg;
4195
4196         
4197     },
4198     getHeaderChildContainer : function()
4199     {
4200         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4201             return this.el.select('.navbar-header',true).first();
4202         }
4203         
4204         return this.getChildContainer();
4205     },
4206     
4207     
4208     initEvents : function()
4209     {
4210         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4211         
4212         if (this.autohide) {
4213             
4214             var prevScroll = 0;
4215             var ft = this.el;
4216             
4217             Roo.get(document).on('scroll',function(e) {
4218                 var ns = Roo.get(document).getScroll().top;
4219                 var os = prevScroll;
4220                 prevScroll = ns;
4221                 
4222                 if(ns > os){
4223                     ft.removeClass('slideDown');
4224                     ft.addClass('slideUp');
4225                     return;
4226                 }
4227                 ft.removeClass('slideUp');
4228                 ft.addClass('slideDown');
4229                  
4230               
4231           },this);
4232         }
4233     }    
4234     
4235 });
4236
4237
4238
4239  
4240
4241  /*
4242  * - LGPL
4243  *
4244  * navbar
4245  * 
4246  */
4247
4248 /**
4249  * @class Roo.bootstrap.NavSidebar
4250  * @extends Roo.bootstrap.Navbar
4251  * Bootstrap Sidebar class
4252  * 
4253  * @constructor
4254  * Create a new Sidebar
4255  * @param {Object} config The config object
4256  */
4257
4258
4259 Roo.bootstrap.NavSidebar = function(config){
4260     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4261 };
4262
4263 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4264     
4265     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4266     
4267     getAutoCreate : function(){
4268         
4269         
4270         return  {
4271             tag: 'div',
4272             cls: 'sidebar sidebar-nav'
4273         };
4274     
4275         
4276     }
4277     
4278     
4279     
4280 });
4281
4282
4283
4284  
4285
4286  /*
4287  * - LGPL
4288  *
4289  * nav group
4290  * 
4291  */
4292
4293 /**
4294  * @class Roo.bootstrap.NavGroup
4295  * @extends Roo.bootstrap.Component
4296  * Bootstrap NavGroup class
4297  * @cfg {String} align (left|right)
4298  * @cfg {Boolean} inverse
4299  * @cfg {String} type (nav|pills|tab) default nav
4300  * @cfg {String} navId - reference Id for navbar.
4301
4302  * 
4303  * @constructor
4304  * Create a new nav group
4305  * @param {Object} config The config object
4306  */
4307
4308 Roo.bootstrap.NavGroup = function(config){
4309     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4310     this.navItems = [];
4311    
4312     Roo.bootstrap.NavGroup.register(this);
4313      this.addEvents({
4314         /**
4315              * @event changed
4316              * Fires when the active item changes
4317              * @param {Roo.bootstrap.NavGroup} this
4318              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4319              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4320          */
4321         'changed': true
4322      });
4323     
4324 };
4325
4326 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4327     
4328     align: '',
4329     inverse: false,
4330     form: false,
4331     type: 'nav',
4332     navId : '',
4333     // private
4334     
4335     navItems : false, 
4336     
4337     getAutoCreate : function()
4338     {
4339         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4340         
4341         cfg = {
4342             tag : 'ul',
4343             cls: 'nav' 
4344         };
4345         
4346         if (['tabs','pills'].indexOf(this.type)!==-1) {
4347             cfg.cls += ' nav-' + this.type
4348         } else {
4349             if (this.type!=='nav') {
4350                 Roo.log('nav type must be nav/tabs/pills')
4351             }
4352             cfg.cls += ' navbar-nav'
4353         }
4354         
4355         if (this.parent() && this.parent().sidebar) {
4356             cfg = {
4357                 tag: 'ul',
4358                 cls: 'dashboard-menu sidebar-menu'
4359             };
4360             
4361             return cfg;
4362         }
4363         
4364         if (this.form === true) {
4365             cfg = {
4366                 tag: 'form',
4367                 cls: 'navbar-form'
4368             };
4369             
4370             if (this.align === 'right') {
4371                 cfg.cls += ' navbar-right ml-md-auto';
4372             } else {
4373                 cfg.cls += ' navbar-left';
4374             }
4375         }
4376         
4377         if (this.align === 'right') {
4378             cfg.cls += ' navbar-right ml-md-auto';
4379         } else {
4380             cfg.cls += ' mr-auto';
4381         }
4382         
4383         if (this.inverse) {
4384             cfg.cls += ' navbar-inverse';
4385             
4386         }
4387         
4388         
4389         return cfg;
4390     },
4391     /**
4392     * sets the active Navigation item
4393     * @param {Roo.bootstrap.NavItem} the new current navitem
4394     */
4395     setActiveItem : function(item)
4396     {
4397         var prev = false;
4398         Roo.each(this.navItems, function(v){
4399             if (v == item) {
4400                 return ;
4401             }
4402             if (v.isActive()) {
4403                 v.setActive(false, true);
4404                 prev = v;
4405                 
4406             }
4407             
4408         });
4409
4410         item.setActive(true, true);
4411         this.fireEvent('changed', this, item, prev);
4412         
4413         
4414     },
4415     /**
4416     * gets the active Navigation item
4417     * @return {Roo.bootstrap.NavItem} the current navitem
4418     */
4419     getActive : function()
4420     {
4421         
4422         var prev = false;
4423         Roo.each(this.navItems, function(v){
4424             
4425             if (v.isActive()) {
4426                 prev = v;
4427                 
4428             }
4429             
4430         });
4431         return prev;
4432     },
4433     
4434     indexOfNav : function()
4435     {
4436         
4437         var prev = false;
4438         Roo.each(this.navItems, function(v,i){
4439             
4440             if (v.isActive()) {
4441                 prev = i;
4442                 
4443             }
4444             
4445         });
4446         return prev;
4447     },
4448     /**
4449     * adds a Navigation item
4450     * @param {Roo.bootstrap.NavItem} the navitem to add
4451     */
4452     addItem : function(cfg)
4453     {
4454         var cn = new Roo.bootstrap.NavItem(cfg);
4455         this.register(cn);
4456         cn.parentId = this.id;
4457         cn.onRender(this.el, null);
4458         return cn;
4459     },
4460     /**
4461     * register a Navigation item
4462     * @param {Roo.bootstrap.NavItem} the navitem to add
4463     */
4464     register : function(item)
4465     {
4466         this.navItems.push( item);
4467         item.navId = this.navId;
4468     
4469     },
4470     
4471     /**
4472     * clear all the Navigation item
4473     */
4474    
4475     clearAll : function()
4476     {
4477         this.navItems = [];
4478         this.el.dom.innerHTML = '';
4479     },
4480     
4481     getNavItem: function(tabId)
4482     {
4483         var ret = false;
4484         Roo.each(this.navItems, function(e) {
4485             if (e.tabId == tabId) {
4486                ret =  e;
4487                return false;
4488             }
4489             return true;
4490             
4491         });
4492         return ret;
4493     },
4494     
4495     setActiveNext : function()
4496     {
4497         var i = this.indexOfNav(this.getActive());
4498         if (i > this.navItems.length) {
4499             return;
4500         }
4501         this.setActiveItem(this.navItems[i+1]);
4502     },
4503     setActivePrev : function()
4504     {
4505         var i = this.indexOfNav(this.getActive());
4506         if (i  < 1) {
4507             return;
4508         }
4509         this.setActiveItem(this.navItems[i-1]);
4510     },
4511     clearWasActive : function(except) {
4512         Roo.each(this.navItems, function(e) {
4513             if (e.tabId != except.tabId && e.was_active) {
4514                e.was_active = false;
4515                return false;
4516             }
4517             return true;
4518             
4519         });
4520     },
4521     getWasActive : function ()
4522     {
4523         var r = false;
4524         Roo.each(this.navItems, function(e) {
4525             if (e.was_active) {
4526                r = e;
4527                return false;
4528             }
4529             return true;
4530             
4531         });
4532         return r;
4533     }
4534     
4535     
4536 });
4537
4538  
4539 Roo.apply(Roo.bootstrap.NavGroup, {
4540     
4541     groups: {},
4542      /**
4543     * register a Navigation Group
4544     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4545     */
4546     register : function(navgrp)
4547     {
4548         this.groups[navgrp.navId] = navgrp;
4549         
4550     },
4551     /**
4552     * fetch a Navigation Group based on the navigation ID
4553     * @param {string} the navgroup to add
4554     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4555     */
4556     get: function(navId) {
4557         if (typeof(this.groups[navId]) == 'undefined') {
4558             return false;
4559             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4560         }
4561         return this.groups[navId] ;
4562     }
4563     
4564     
4565     
4566 });
4567
4568  /*
4569  * - LGPL
4570  *
4571  * row
4572  * 
4573  */
4574
4575 /**
4576  * @class Roo.bootstrap.NavItem
4577  * @extends Roo.bootstrap.Component
4578  * Bootstrap Navbar.NavItem class
4579  * @cfg {String} href  link to
4580  * @cfg {String} html content of button
4581  * @cfg {String} badge text inside badge
4582  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4583  * @cfg {String} glyphicon DEPRICATED - use fa
4584  * @cfg {String} icon DEPRICATED - use fa
4585  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4586  * @cfg {Boolean} active Is item active
4587  * @cfg {Boolean} disabled Is item disabled
4588  
4589  * @cfg {Boolean} preventDefault (true | false) default false
4590  * @cfg {String} tabId the tab that this item activates.
4591  * @cfg {String} tagtype (a|span) render as a href or span?
4592  * @cfg {Boolean} animateRef (true|false) link to element default false  
4593   
4594  * @constructor
4595  * Create a new Navbar Item
4596  * @param {Object} config The config object
4597  */
4598 Roo.bootstrap.NavItem = function(config){
4599     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4600     this.addEvents({
4601         // raw events
4602         /**
4603          * @event click
4604          * The raw click event for the entire grid.
4605          * @param {Roo.EventObject} e
4606          */
4607         "click" : true,
4608          /**
4609             * @event changed
4610             * Fires when the active item active state changes
4611             * @param {Roo.bootstrap.NavItem} this
4612             * @param {boolean} state the new state
4613              
4614          */
4615         'changed': true,
4616         /**
4617             * @event scrollto
4618             * Fires when scroll to element
4619             * @param {Roo.bootstrap.NavItem} this
4620             * @param {Object} options
4621             * @param {Roo.EventObject} e
4622              
4623          */
4624         'scrollto': true
4625     });
4626    
4627 };
4628
4629 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4630     
4631     href: false,
4632     html: '',
4633     badge: '',
4634     icon: false,
4635     fa : false,
4636     glyphicon: false,
4637     active: false,
4638     preventDefault : false,
4639     tabId : false,
4640     tagtype : 'a',
4641     disabled : false,
4642     animateRef : false,
4643     was_active : false,
4644     
4645     getAutoCreate : function(){
4646          
4647         var cfg = {
4648             tag: 'li',
4649             cls: 'nav-item'
4650             
4651         };
4652         
4653         if (this.active) {
4654             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4655         }
4656         if (this.disabled) {
4657             cfg.cls += ' disabled';
4658         }
4659         
4660         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4661             cfg.cn = [
4662                 {
4663                     tag: this.tagtype,
4664                     href : this.href || "#",
4665                     html: this.html || ''
4666                 }
4667             ];
4668             if (this.tagtype == 'a') {
4669                 cfg.cn[0].cls = 'nav-link';
4670             }
4671             if (this.icon) {
4672                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4673             }
4674             if (this.fa) {
4675                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4676             }
4677             if(this.glyphicon) {
4678                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4679             }
4680             
4681             if (this.menu) {
4682                 
4683                 cfg.cn[0].html += " <span class='caret'></span>";
4684              
4685             }
4686             
4687             if (this.badge !== '') {
4688                  
4689                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4690             }
4691         }
4692         
4693         
4694         
4695         return cfg;
4696     },
4697     initEvents: function() 
4698     {
4699         if (typeof (this.menu) != 'undefined') {
4700             this.menu.parentType = this.xtype;
4701             this.menu.triggerEl = this.el;
4702             this.menu = this.addxtype(Roo.apply({}, this.menu));
4703         }
4704         
4705         this.el.select('a',true).on('click', this.onClick, this);
4706         
4707         if(this.tagtype == 'span'){
4708             this.el.select('span',true).on('click', this.onClick, this);
4709         }
4710        
4711         // at this point parent should be available..
4712         this.parent().register(this);
4713     },
4714     
4715     onClick : function(e)
4716     {
4717         if (e.getTarget('.dropdown-menu-item')) {
4718             // did you click on a menu itemm.... - then don't trigger onclick..
4719             return;
4720         }
4721         
4722         if(
4723                 this.preventDefault || 
4724                 this.href == '#' 
4725         ){
4726             Roo.log("NavItem - prevent Default?");
4727             e.preventDefault();
4728         }
4729         
4730         if (this.disabled) {
4731             return;
4732         }
4733         
4734         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4735         if (tg && tg.transition) {
4736             Roo.log("waiting for the transitionend");
4737             return;
4738         }
4739         
4740         
4741         
4742         //Roo.log("fire event clicked");
4743         if(this.fireEvent('click', this, e) === false){
4744             return;
4745         };
4746         
4747         if(this.tagtype == 'span'){
4748             return;
4749         }
4750         
4751         //Roo.log(this.href);
4752         var ael = this.el.select('a',true).first();
4753         //Roo.log(ael);
4754         
4755         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4756             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4757             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4758                 return; // ignore... - it's a 'hash' to another page.
4759             }
4760             Roo.log("NavItem - prevent Default?");
4761             e.preventDefault();
4762             this.scrollToElement(e);
4763         }
4764         
4765         
4766         var p =  this.parent();
4767    
4768         if (['tabs','pills'].indexOf(p.type)!==-1) {
4769             if (typeof(p.setActiveItem) !== 'undefined') {
4770                 p.setActiveItem(this);
4771             }
4772         }
4773         
4774         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4775         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4776             // remove the collapsed menu expand...
4777             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4778         }
4779     },
4780     
4781     isActive: function () {
4782         return this.active
4783     },
4784     setActive : function(state, fire, is_was_active)
4785     {
4786         if (this.active && !state && this.navId) {
4787             this.was_active = true;
4788             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4789             if (nv) {
4790                 nv.clearWasActive(this);
4791             }
4792             
4793         }
4794         this.active = state;
4795         
4796         if (!state ) {
4797             this.el.removeClass('active');
4798         } else if (!this.el.hasClass('active')) {
4799             this.el.addClass('active');
4800         }
4801         if (fire) {
4802             this.fireEvent('changed', this, state);
4803         }
4804         
4805         // show a panel if it's registered and related..
4806         
4807         if (!this.navId || !this.tabId || !state || is_was_active) {
4808             return;
4809         }
4810         
4811         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4812         if (!tg) {
4813             return;
4814         }
4815         var pan = tg.getPanelByName(this.tabId);
4816         if (!pan) {
4817             return;
4818         }
4819         // if we can not flip to new panel - go back to old nav highlight..
4820         if (false == tg.showPanel(pan)) {
4821             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4822             if (nv) {
4823                 var onav = nv.getWasActive();
4824                 if (onav) {
4825                     onav.setActive(true, false, true);
4826                 }
4827             }
4828             
4829         }
4830         
4831         
4832         
4833     },
4834      // this should not be here...
4835     setDisabled : function(state)
4836     {
4837         this.disabled = state;
4838         if (!state ) {
4839             this.el.removeClass('disabled');
4840         } else if (!this.el.hasClass('disabled')) {
4841             this.el.addClass('disabled');
4842         }
4843         
4844     },
4845     
4846     /**
4847      * Fetch the element to display the tooltip on.
4848      * @return {Roo.Element} defaults to this.el
4849      */
4850     tooltipEl : function()
4851     {
4852         return this.el.select('' + this.tagtype + '', true).first();
4853     },
4854     
4855     scrollToElement : function(e)
4856     {
4857         var c = document.body;
4858         
4859         /*
4860          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4861          */
4862         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4863             c = document.documentElement;
4864         }
4865         
4866         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4867         
4868         if(!target){
4869             return;
4870         }
4871
4872         var o = target.calcOffsetsTo(c);
4873         
4874         var options = {
4875             target : target,
4876             value : o[1]
4877         };
4878         
4879         this.fireEvent('scrollto', this, options, e);
4880         
4881         Roo.get(c).scrollTo('top', options.value, true);
4882         
4883         return;
4884     }
4885 });
4886  
4887
4888  /*
4889  * - LGPL
4890  *
4891  * sidebar item
4892  *
4893  *  li
4894  *    <span> icon </span>
4895  *    <span> text </span>
4896  *    <span>badge </span>
4897  */
4898
4899 /**
4900  * @class Roo.bootstrap.NavSidebarItem
4901  * @extends Roo.bootstrap.NavItem
4902  * Bootstrap Navbar.NavSidebarItem class
4903  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4904  * {Boolean} open is the menu open
4905  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4906  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4907  * {String} buttonSize (sm|md|lg)the extra classes for the button
4908  * {Boolean} showArrow show arrow next to the text (default true)
4909  * @constructor
4910  * Create a new Navbar Button
4911  * @param {Object} config The config object
4912  */
4913 Roo.bootstrap.NavSidebarItem = function(config){
4914     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4915     this.addEvents({
4916         // raw events
4917         /**
4918          * @event click
4919          * The raw click event for the entire grid.
4920          * @param {Roo.EventObject} e
4921          */
4922         "click" : true,
4923          /**
4924             * @event changed
4925             * Fires when the active item active state changes
4926             * @param {Roo.bootstrap.NavSidebarItem} this
4927             * @param {boolean} state the new state
4928              
4929          */
4930         'changed': true
4931     });
4932    
4933 };
4934
4935 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4936     
4937     badgeWeight : 'default',
4938     
4939     open: false,
4940     
4941     buttonView : false,
4942     
4943     buttonWeight : 'default',
4944     
4945     buttonSize : 'md',
4946     
4947     showArrow : true,
4948     
4949     getAutoCreate : function(){
4950         
4951         
4952         var a = {
4953                 tag: 'a',
4954                 href : this.href || '#',
4955                 cls: '',
4956                 html : '',
4957                 cn : []
4958         };
4959         
4960         if(this.buttonView){
4961             a = {
4962                 tag: 'button',
4963                 href : this.href || '#',
4964                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4965                 html : this.html,
4966                 cn : []
4967             };
4968         }
4969         
4970         var cfg = {
4971             tag: 'li',
4972             cls: '',
4973             cn: [ a ]
4974         };
4975         
4976         if (this.active) {
4977             cfg.cls += ' active';
4978         }
4979         
4980         if (this.disabled) {
4981             cfg.cls += ' disabled';
4982         }
4983         if (this.open) {
4984             cfg.cls += ' open x-open';
4985         }
4986         // left icon..
4987         if (this.glyphicon || this.icon) {
4988             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4989             a.cn.push({ tag : 'i', cls : c }) ;
4990         }
4991         
4992         if(!this.buttonView){
4993             var span = {
4994                 tag: 'span',
4995                 html : this.html || ''
4996             };
4997
4998             a.cn.push(span);
4999             
5000         }
5001         
5002         if (this.badge !== '') {
5003             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5004         }
5005         
5006         if (this.menu) {
5007             
5008             if(this.showArrow){
5009                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5010             }
5011             
5012             a.cls += ' dropdown-toggle treeview' ;
5013         }
5014         
5015         return cfg;
5016     },
5017     
5018     initEvents : function()
5019     { 
5020         if (typeof (this.menu) != 'undefined') {
5021             this.menu.parentType = this.xtype;
5022             this.menu.triggerEl = this.el;
5023             this.menu = this.addxtype(Roo.apply({}, this.menu));
5024         }
5025         
5026         this.el.on('click', this.onClick, this);
5027         
5028         if(this.badge !== ''){
5029             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5030         }
5031         
5032     },
5033     
5034     onClick : function(e)
5035     {
5036         if(this.disabled){
5037             e.preventDefault();
5038             return;
5039         }
5040         
5041         if(this.preventDefault){
5042             e.preventDefault();
5043         }
5044         
5045         this.fireEvent('click', this);
5046     },
5047     
5048     disable : function()
5049     {
5050         this.setDisabled(true);
5051     },
5052     
5053     enable : function()
5054     {
5055         this.setDisabled(false);
5056     },
5057     
5058     setDisabled : function(state)
5059     {
5060         if(this.disabled == state){
5061             return;
5062         }
5063         
5064         this.disabled = state;
5065         
5066         if (state) {
5067             this.el.addClass('disabled');
5068             return;
5069         }
5070         
5071         this.el.removeClass('disabled');
5072         
5073         return;
5074     },
5075     
5076     setActive : function(state)
5077     {
5078         if(this.active == state){
5079             return;
5080         }
5081         
5082         this.active = state;
5083         
5084         if (state) {
5085             this.el.addClass('active');
5086             return;
5087         }
5088         
5089         this.el.removeClass('active');
5090         
5091         return;
5092     },
5093     
5094     isActive: function () 
5095     {
5096         return this.active;
5097     },
5098     
5099     setBadge : function(str)
5100     {
5101         if(!this.badgeEl){
5102             return;
5103         }
5104         
5105         this.badgeEl.dom.innerHTML = str;
5106     }
5107     
5108    
5109      
5110  
5111 });
5112  
5113
5114  /*
5115  * - LGPL
5116  *
5117  * row
5118  * 
5119  */
5120
5121 /**
5122  * @class Roo.bootstrap.Row
5123  * @extends Roo.bootstrap.Component
5124  * Bootstrap Row class (contains columns...)
5125  * 
5126  * @constructor
5127  * Create a new Row
5128  * @param {Object} config The config object
5129  */
5130
5131 Roo.bootstrap.Row = function(config){
5132     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5133 };
5134
5135 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5136     
5137     getAutoCreate : function(){
5138        return {
5139             cls: 'row clearfix'
5140        };
5141     }
5142     
5143     
5144 });
5145
5146  
5147
5148  /*
5149  * - LGPL
5150  *
5151  * element
5152  * 
5153  */
5154
5155 /**
5156  * @class Roo.bootstrap.Element
5157  * @extends Roo.bootstrap.Component
5158  * Bootstrap Element class
5159  * @cfg {String} html contents of the element
5160  * @cfg {String} tag tag of the element
5161  * @cfg {String} cls class of the element
5162  * @cfg {Boolean} preventDefault (true|false) default false
5163  * @cfg {Boolean} clickable (true|false) default false
5164  * 
5165  * @constructor
5166  * Create a new Element
5167  * @param {Object} config The config object
5168  */
5169
5170 Roo.bootstrap.Element = function(config){
5171     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5172     
5173     this.addEvents({
5174         // raw events
5175         /**
5176          * @event click
5177          * When a element is chick
5178          * @param {Roo.bootstrap.Element} this
5179          * @param {Roo.EventObject} e
5180          */
5181         "click" : true
5182     });
5183 };
5184
5185 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5186     
5187     tag: 'div',
5188     cls: '',
5189     html: '',
5190     preventDefault: false, 
5191     clickable: false,
5192     
5193     getAutoCreate : function(){
5194         
5195         var cfg = {
5196             tag: this.tag,
5197             // cls: this.cls, double assign in parent class Component.js :: onRender
5198             html: this.html
5199         };
5200         
5201         return cfg;
5202     },
5203     
5204     initEvents: function() 
5205     {
5206         Roo.bootstrap.Element.superclass.initEvents.call(this);
5207         
5208         if(this.clickable){
5209             this.el.on('click', this.onClick, this);
5210         }
5211         
5212     },
5213     
5214     onClick : function(e)
5215     {
5216         if(this.preventDefault){
5217             e.preventDefault();
5218         }
5219         
5220         this.fireEvent('click', this, e);
5221     },
5222     
5223     getValue : function()
5224     {
5225         return this.el.dom.innerHTML;
5226     },
5227     
5228     setValue : function(value)
5229     {
5230         this.el.dom.innerHTML = value;
5231     }
5232    
5233 });
5234
5235  
5236
5237  /*
5238  * - LGPL
5239  *
5240  * pagination
5241  * 
5242  */
5243
5244 /**
5245  * @class Roo.bootstrap.Pagination
5246  * @extends Roo.bootstrap.Component
5247  * Bootstrap Pagination class
5248  * @cfg {String} size xs | sm | md | lg
5249  * @cfg {Boolean} inverse false | true
5250  * 
5251  * @constructor
5252  * Create a new Pagination
5253  * @param {Object} config The config object
5254  */
5255
5256 Roo.bootstrap.Pagination = function(config){
5257     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5258 };
5259
5260 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5261     
5262     cls: false,
5263     size: false,
5264     inverse: false,
5265     
5266     getAutoCreate : function(){
5267         var cfg = {
5268             tag: 'ul',
5269                 cls: 'pagination'
5270         };
5271         if (this.inverse) {
5272             cfg.cls += ' inverse';
5273         }
5274         if (this.html) {
5275             cfg.html=this.html;
5276         }
5277         if (this.cls) {
5278             cfg.cls += " " + this.cls;
5279         }
5280         return cfg;
5281     }
5282    
5283 });
5284
5285  
5286
5287  /*
5288  * - LGPL
5289  *
5290  * Pagination item
5291  * 
5292  */
5293
5294
5295 /**
5296  * @class Roo.bootstrap.PaginationItem
5297  * @extends Roo.bootstrap.Component
5298  * Bootstrap PaginationItem class
5299  * @cfg {String} html text
5300  * @cfg {String} href the link
5301  * @cfg {Boolean} preventDefault (true | false) default true
5302  * @cfg {Boolean} active (true | false) default false
5303  * @cfg {Boolean} disabled default false
5304  * 
5305  * 
5306  * @constructor
5307  * Create a new PaginationItem
5308  * @param {Object} config The config object
5309  */
5310
5311
5312 Roo.bootstrap.PaginationItem = function(config){
5313     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5314     this.addEvents({
5315         // raw events
5316         /**
5317          * @event click
5318          * The raw click event for the entire grid.
5319          * @param {Roo.EventObject} e
5320          */
5321         "click" : true
5322     });
5323 };
5324
5325 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5326     
5327     href : false,
5328     html : false,
5329     preventDefault: true,
5330     active : false,
5331     cls : false,
5332     disabled: false,
5333     
5334     getAutoCreate : function(){
5335         var cfg= {
5336             tag: 'li',
5337             cn: [
5338                 {
5339                     tag : 'a',
5340                     href : this.href ? this.href : '#',
5341                     html : this.html ? this.html : ''
5342                 }
5343             ]
5344         };
5345         
5346         if(this.cls){
5347             cfg.cls = this.cls;
5348         }
5349         
5350         if(this.disabled){
5351             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5352         }
5353         
5354         if(this.active){
5355             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5356         }
5357         
5358         return cfg;
5359     },
5360     
5361     initEvents: function() {
5362         
5363         this.el.on('click', this.onClick, this);
5364         
5365     },
5366     onClick : function(e)
5367     {
5368         Roo.log('PaginationItem on click ');
5369         if(this.preventDefault){
5370             e.preventDefault();
5371         }
5372         
5373         if(this.disabled){
5374             return;
5375         }
5376         
5377         this.fireEvent('click', this, e);
5378     }
5379    
5380 });
5381
5382  
5383
5384  /*
5385  * - LGPL
5386  *
5387  * slider
5388  * 
5389  */
5390
5391
5392 /**
5393  * @class Roo.bootstrap.Slider
5394  * @extends Roo.bootstrap.Component
5395  * Bootstrap Slider class
5396  *    
5397  * @constructor
5398  * Create a new Slider
5399  * @param {Object} config The config object
5400  */
5401
5402 Roo.bootstrap.Slider = function(config){
5403     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5404 };
5405
5406 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5407     
5408     getAutoCreate : function(){
5409         
5410         var cfg = {
5411             tag: 'div',
5412             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5413             cn: [
5414                 {
5415                     tag: 'a',
5416                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5417                 }
5418             ]
5419         };
5420         
5421         return cfg;
5422     }
5423    
5424 });
5425
5426  /*
5427  * Based on:
5428  * Ext JS Library 1.1.1
5429  * Copyright(c) 2006-2007, Ext JS, LLC.
5430  *
5431  * Originally Released Under LGPL - original licence link has changed is not relivant.
5432  *
5433  * Fork - LGPL
5434  * <script type="text/javascript">
5435  */
5436  
5437
5438 /**
5439  * @class Roo.grid.ColumnModel
5440  * @extends Roo.util.Observable
5441  * This is the default implementation of a ColumnModel used by the Grid. It defines
5442  * the columns in the grid.
5443  * <br>Usage:<br>
5444  <pre><code>
5445  var colModel = new Roo.grid.ColumnModel([
5446         {header: "Ticker", width: 60, sortable: true, locked: true},
5447         {header: "Company Name", width: 150, sortable: true},
5448         {header: "Market Cap.", width: 100, sortable: true},
5449         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5450         {header: "Employees", width: 100, sortable: true, resizable: false}
5451  ]);
5452  </code></pre>
5453  * <p>
5454  
5455  * The config options listed for this class are options which may appear in each
5456  * individual column definition.
5457  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5458  * @constructor
5459  * @param {Object} config An Array of column config objects. See this class's
5460  * config objects for details.
5461 */
5462 Roo.grid.ColumnModel = function(config){
5463         /**
5464      * The config passed into the constructor
5465      */
5466     this.config = config;
5467     this.lookup = {};
5468
5469     // if no id, create one
5470     // if the column does not have a dataIndex mapping,
5471     // map it to the order it is in the config
5472     for(var i = 0, len = config.length; i < len; i++){
5473         var c = config[i];
5474         if(typeof c.dataIndex == "undefined"){
5475             c.dataIndex = i;
5476         }
5477         if(typeof c.renderer == "string"){
5478             c.renderer = Roo.util.Format[c.renderer];
5479         }
5480         if(typeof c.id == "undefined"){
5481             c.id = Roo.id();
5482         }
5483         if(c.editor && c.editor.xtype){
5484             c.editor  = Roo.factory(c.editor, Roo.grid);
5485         }
5486         if(c.editor && c.editor.isFormField){
5487             c.editor = new Roo.grid.GridEditor(c.editor);
5488         }
5489         this.lookup[c.id] = c;
5490     }
5491
5492     /**
5493      * The width of columns which have no width specified (defaults to 100)
5494      * @type Number
5495      */
5496     this.defaultWidth = 100;
5497
5498     /**
5499      * Default sortable of columns which have no sortable specified (defaults to false)
5500      * @type Boolean
5501      */
5502     this.defaultSortable = false;
5503
5504     this.addEvents({
5505         /**
5506              * @event widthchange
5507              * Fires when the width of a column changes.
5508              * @param {ColumnModel} this
5509              * @param {Number} columnIndex The column index
5510              * @param {Number} newWidth The new width
5511              */
5512             "widthchange": true,
5513         /**
5514              * @event headerchange
5515              * Fires when the text of a header changes.
5516              * @param {ColumnModel} this
5517              * @param {Number} columnIndex The column index
5518              * @param {Number} newText The new header text
5519              */
5520             "headerchange": true,
5521         /**
5522              * @event hiddenchange
5523              * Fires when a column is hidden or "unhidden".
5524              * @param {ColumnModel} this
5525              * @param {Number} columnIndex The column index
5526              * @param {Boolean} hidden true if hidden, false otherwise
5527              */
5528             "hiddenchange": true,
5529             /**
5530          * @event columnmoved
5531          * Fires when a column is moved.
5532          * @param {ColumnModel} this
5533          * @param {Number} oldIndex
5534          * @param {Number} newIndex
5535          */
5536         "columnmoved" : true,
5537         /**
5538          * @event columlockchange
5539          * Fires when a column's locked state is changed
5540          * @param {ColumnModel} this
5541          * @param {Number} colIndex
5542          * @param {Boolean} locked true if locked
5543          */
5544         "columnlockchange" : true
5545     });
5546     Roo.grid.ColumnModel.superclass.constructor.call(this);
5547 };
5548 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5549     /**
5550      * @cfg {String} header The header text to display in the Grid view.
5551      */
5552     /**
5553      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5554      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5555      * specified, the column's index is used as an index into the Record's data Array.
5556      */
5557     /**
5558      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5559      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5560      */
5561     /**
5562      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5563      * Defaults to the value of the {@link #defaultSortable} property.
5564      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5565      */
5566     /**
5567      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5568      */
5569     /**
5570      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5571      */
5572     /**
5573      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5574      */
5575     /**
5576      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5577      */
5578     /**
5579      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5580      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5581      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5582      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5583      */
5584        /**
5585      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5586      */
5587     /**
5588      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5589      */
5590     /**
5591      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5592      */
5593     /**
5594      * @cfg {String} cursor (Optional)
5595      */
5596     /**
5597      * @cfg {String} tooltip (Optional)
5598      */
5599     /**
5600      * @cfg {Number} xs (Optional)
5601      */
5602     /**
5603      * @cfg {Number} sm (Optional)
5604      */
5605     /**
5606      * @cfg {Number} md (Optional)
5607      */
5608     /**
5609      * @cfg {Number} lg (Optional)
5610      */
5611     /**
5612      * Returns the id of the column at the specified index.
5613      * @param {Number} index The column index
5614      * @return {String} the id
5615      */
5616     getColumnId : function(index){
5617         return this.config[index].id;
5618     },
5619
5620     /**
5621      * Returns the column for a specified id.
5622      * @param {String} id The column id
5623      * @return {Object} the column
5624      */
5625     getColumnById : function(id){
5626         return this.lookup[id];
5627     },
5628
5629     
5630     /**
5631      * Returns the column for a specified dataIndex.
5632      * @param {String} dataIndex The column dataIndex
5633      * @return {Object|Boolean} the column or false if not found
5634      */
5635     getColumnByDataIndex: function(dataIndex){
5636         var index = this.findColumnIndex(dataIndex);
5637         return index > -1 ? this.config[index] : false;
5638     },
5639     
5640     /**
5641      * Returns the index for a specified column id.
5642      * @param {String} id The column id
5643      * @return {Number} the index, or -1 if not found
5644      */
5645     getIndexById : function(id){
5646         for(var i = 0, len = this.config.length; i < len; i++){
5647             if(this.config[i].id == id){
5648                 return i;
5649             }
5650         }
5651         return -1;
5652     },
5653     
5654     /**
5655      * Returns the index for a specified column dataIndex.
5656      * @param {String} dataIndex The column dataIndex
5657      * @return {Number} the index, or -1 if not found
5658      */
5659     
5660     findColumnIndex : function(dataIndex){
5661         for(var i = 0, len = this.config.length; i < len; i++){
5662             if(this.config[i].dataIndex == dataIndex){
5663                 return i;
5664             }
5665         }
5666         return -1;
5667     },
5668     
5669     
5670     moveColumn : function(oldIndex, newIndex){
5671         var c = this.config[oldIndex];
5672         this.config.splice(oldIndex, 1);
5673         this.config.splice(newIndex, 0, c);
5674         this.dataMap = null;
5675         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5676     },
5677
5678     isLocked : function(colIndex){
5679         return this.config[colIndex].locked === true;
5680     },
5681
5682     setLocked : function(colIndex, value, suppressEvent){
5683         if(this.isLocked(colIndex) == value){
5684             return;
5685         }
5686         this.config[colIndex].locked = value;
5687         if(!suppressEvent){
5688             this.fireEvent("columnlockchange", this, colIndex, value);
5689         }
5690     },
5691
5692     getTotalLockedWidth : function(){
5693         var totalWidth = 0;
5694         for(var i = 0; i < this.config.length; i++){
5695             if(this.isLocked(i) && !this.isHidden(i)){
5696                 this.totalWidth += this.getColumnWidth(i);
5697             }
5698         }
5699         return totalWidth;
5700     },
5701
5702     getLockedCount : function(){
5703         for(var i = 0, len = this.config.length; i < len; i++){
5704             if(!this.isLocked(i)){
5705                 return i;
5706             }
5707         }
5708         
5709         return this.config.length;
5710     },
5711
5712     /**
5713      * Returns the number of columns.
5714      * @return {Number}
5715      */
5716     getColumnCount : function(visibleOnly){
5717         if(visibleOnly === true){
5718             var c = 0;
5719             for(var i = 0, len = this.config.length; i < len; i++){
5720                 if(!this.isHidden(i)){
5721                     c++;
5722                 }
5723             }
5724             return c;
5725         }
5726         return this.config.length;
5727     },
5728
5729     /**
5730      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5731      * @param {Function} fn
5732      * @param {Object} scope (optional)
5733      * @return {Array} result
5734      */
5735     getColumnsBy : function(fn, scope){
5736         var r = [];
5737         for(var i = 0, len = this.config.length; i < len; i++){
5738             var c = this.config[i];
5739             if(fn.call(scope||this, c, i) === true){
5740                 r[r.length] = c;
5741             }
5742         }
5743         return r;
5744     },
5745
5746     /**
5747      * Returns true if the specified column is sortable.
5748      * @param {Number} col The column index
5749      * @return {Boolean}
5750      */
5751     isSortable : function(col){
5752         if(typeof this.config[col].sortable == "undefined"){
5753             return this.defaultSortable;
5754         }
5755         return this.config[col].sortable;
5756     },
5757
5758     /**
5759      * Returns the rendering (formatting) function defined for the column.
5760      * @param {Number} col The column index.
5761      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5762      */
5763     getRenderer : function(col){
5764         if(!this.config[col].renderer){
5765             return Roo.grid.ColumnModel.defaultRenderer;
5766         }
5767         return this.config[col].renderer;
5768     },
5769
5770     /**
5771      * Sets the rendering (formatting) function for a column.
5772      * @param {Number} col The column index
5773      * @param {Function} fn The function to use to process the cell's raw data
5774      * to return HTML markup for the grid view. The render function is called with
5775      * the following parameters:<ul>
5776      * <li>Data value.</li>
5777      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5778      * <li>css A CSS style string to apply to the table cell.</li>
5779      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5780      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5781      * <li>Row index</li>
5782      * <li>Column index</li>
5783      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5784      */
5785     setRenderer : function(col, fn){
5786         this.config[col].renderer = fn;
5787     },
5788
5789     /**
5790      * Returns the width for the specified column.
5791      * @param {Number} col The column index
5792      * @return {Number}
5793      */
5794     getColumnWidth : function(col){
5795         return this.config[col].width * 1 || this.defaultWidth;
5796     },
5797
5798     /**
5799      * Sets the width for a column.
5800      * @param {Number} col The column index
5801      * @param {Number} width The new width
5802      */
5803     setColumnWidth : function(col, width, suppressEvent){
5804         this.config[col].width = width;
5805         this.totalWidth = null;
5806         if(!suppressEvent){
5807              this.fireEvent("widthchange", this, col, width);
5808         }
5809     },
5810
5811     /**
5812      * Returns the total width of all columns.
5813      * @param {Boolean} includeHidden True to include hidden column widths
5814      * @return {Number}
5815      */
5816     getTotalWidth : function(includeHidden){
5817         if(!this.totalWidth){
5818             this.totalWidth = 0;
5819             for(var i = 0, len = this.config.length; i < len; i++){
5820                 if(includeHidden || !this.isHidden(i)){
5821                     this.totalWidth += this.getColumnWidth(i);
5822                 }
5823             }
5824         }
5825         return this.totalWidth;
5826     },
5827
5828     /**
5829      * Returns the header for the specified column.
5830      * @param {Number} col The column index
5831      * @return {String}
5832      */
5833     getColumnHeader : function(col){
5834         return this.config[col].header;
5835     },
5836
5837     /**
5838      * Sets the header for a column.
5839      * @param {Number} col The column index
5840      * @param {String} header The new header
5841      */
5842     setColumnHeader : function(col, header){
5843         this.config[col].header = header;
5844         this.fireEvent("headerchange", this, col, header);
5845     },
5846
5847     /**
5848      * Returns the tooltip for the specified column.
5849      * @param {Number} col The column index
5850      * @return {String}
5851      */
5852     getColumnTooltip : function(col){
5853             return this.config[col].tooltip;
5854     },
5855     /**
5856      * Sets the tooltip for a column.
5857      * @param {Number} col The column index
5858      * @param {String} tooltip The new tooltip
5859      */
5860     setColumnTooltip : function(col, tooltip){
5861             this.config[col].tooltip = tooltip;
5862     },
5863
5864     /**
5865      * Returns the dataIndex for the specified column.
5866      * @param {Number} col The column index
5867      * @return {Number}
5868      */
5869     getDataIndex : function(col){
5870         return this.config[col].dataIndex;
5871     },
5872
5873     /**
5874      * Sets the dataIndex for a column.
5875      * @param {Number} col The column index
5876      * @param {Number} dataIndex The new dataIndex
5877      */
5878     setDataIndex : function(col, dataIndex){
5879         this.config[col].dataIndex = dataIndex;
5880     },
5881
5882     
5883     
5884     /**
5885      * Returns true if the cell is editable.
5886      * @param {Number} colIndex The column index
5887      * @param {Number} rowIndex The row index - this is nto actually used..?
5888      * @return {Boolean}
5889      */
5890     isCellEditable : function(colIndex, rowIndex){
5891         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5892     },
5893
5894     /**
5895      * Returns the editor defined for the cell/column.
5896      * return false or null to disable editing.
5897      * @param {Number} colIndex The column index
5898      * @param {Number} rowIndex The row index
5899      * @return {Object}
5900      */
5901     getCellEditor : function(colIndex, rowIndex){
5902         return this.config[colIndex].editor;
5903     },
5904
5905     /**
5906      * Sets if a column is editable.
5907      * @param {Number} col The column index
5908      * @param {Boolean} editable True if the column is editable
5909      */
5910     setEditable : function(col, editable){
5911         this.config[col].editable = editable;
5912     },
5913
5914
5915     /**
5916      * Returns true if the column is hidden.
5917      * @param {Number} colIndex The column index
5918      * @return {Boolean}
5919      */
5920     isHidden : function(colIndex){
5921         return this.config[colIndex].hidden;
5922     },
5923
5924
5925     /**
5926      * Returns true if the column width cannot be changed
5927      */
5928     isFixed : function(colIndex){
5929         return this.config[colIndex].fixed;
5930     },
5931
5932     /**
5933      * Returns true if the column can be resized
5934      * @return {Boolean}
5935      */
5936     isResizable : function(colIndex){
5937         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5938     },
5939     /**
5940      * Sets if a column is hidden.
5941      * @param {Number} colIndex The column index
5942      * @param {Boolean} hidden True if the column is hidden
5943      */
5944     setHidden : function(colIndex, hidden){
5945         this.config[colIndex].hidden = hidden;
5946         this.totalWidth = null;
5947         this.fireEvent("hiddenchange", this, colIndex, hidden);
5948     },
5949
5950     /**
5951      * Sets the editor for a column.
5952      * @param {Number} col The column index
5953      * @param {Object} editor The editor object
5954      */
5955     setEditor : function(col, editor){
5956         this.config[col].editor = editor;
5957     }
5958 });
5959
5960 Roo.grid.ColumnModel.defaultRenderer = function(value)
5961 {
5962     if(typeof value == "object") {
5963         return value;
5964     }
5965         if(typeof value == "string" && value.length < 1){
5966             return "&#160;";
5967         }
5968     
5969         return String.format("{0}", value);
5970 };
5971
5972 // Alias for backwards compatibility
5973 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5974 /*
5975  * Based on:
5976  * Ext JS Library 1.1.1
5977  * Copyright(c) 2006-2007, Ext JS, LLC.
5978  *
5979  * Originally Released Under LGPL - original licence link has changed is not relivant.
5980  *
5981  * Fork - LGPL
5982  * <script type="text/javascript">
5983  */
5984  
5985 /**
5986  * @class Roo.LoadMask
5987  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5988  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5989  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5990  * element's UpdateManager load indicator and will be destroyed after the initial load.
5991  * @constructor
5992  * Create a new LoadMask
5993  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5994  * @param {Object} config The config object
5995  */
5996 Roo.LoadMask = function(el, config){
5997     this.el = Roo.get(el);
5998     Roo.apply(this, config);
5999     if(this.store){
6000         this.store.on('beforeload', this.onBeforeLoad, this);
6001         this.store.on('load', this.onLoad, this);
6002         this.store.on('loadexception', this.onLoadException, this);
6003         this.removeMask = false;
6004     }else{
6005         var um = this.el.getUpdateManager();
6006         um.showLoadIndicator = false; // disable the default indicator
6007         um.on('beforeupdate', this.onBeforeLoad, this);
6008         um.on('update', this.onLoad, this);
6009         um.on('failure', this.onLoad, this);
6010         this.removeMask = true;
6011     }
6012 };
6013
6014 Roo.LoadMask.prototype = {
6015     /**
6016      * @cfg {Boolean} removeMask
6017      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6018      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6019      */
6020     /**
6021      * @cfg {String} msg
6022      * The text to display in a centered loading message box (defaults to 'Loading...')
6023      */
6024     msg : 'Loading...',
6025     /**
6026      * @cfg {String} msgCls
6027      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6028      */
6029     msgCls : 'x-mask-loading',
6030
6031     /**
6032      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6033      * @type Boolean
6034      */
6035     disabled: false,
6036
6037     /**
6038      * Disables the mask to prevent it from being displayed
6039      */
6040     disable : function(){
6041        this.disabled = true;
6042     },
6043
6044     /**
6045      * Enables the mask so that it can be displayed
6046      */
6047     enable : function(){
6048         this.disabled = false;
6049     },
6050     
6051     onLoadException : function()
6052     {
6053         Roo.log(arguments);
6054         
6055         if (typeof(arguments[3]) != 'undefined') {
6056             Roo.MessageBox.alert("Error loading",arguments[3]);
6057         } 
6058         /*
6059         try {
6060             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6061                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6062             }   
6063         } catch(e) {
6064             
6065         }
6066         */
6067     
6068         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6069     },
6070     // private
6071     onLoad : function()
6072     {
6073         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6074     },
6075
6076     // private
6077     onBeforeLoad : function(){
6078         if(!this.disabled){
6079             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6080         }
6081     },
6082
6083     // private
6084     destroy : function(){
6085         if(this.store){
6086             this.store.un('beforeload', this.onBeforeLoad, this);
6087             this.store.un('load', this.onLoad, this);
6088             this.store.un('loadexception', this.onLoadException, this);
6089         }else{
6090             var um = this.el.getUpdateManager();
6091             um.un('beforeupdate', this.onBeforeLoad, this);
6092             um.un('update', this.onLoad, this);
6093             um.un('failure', this.onLoad, this);
6094         }
6095     }
6096 };/*
6097  * - LGPL
6098  *
6099  * table
6100  * 
6101  */
6102
6103 /**
6104  * @class Roo.bootstrap.Table
6105  * @extends Roo.bootstrap.Component
6106  * Bootstrap Table class
6107  * @cfg {String} cls table class
6108  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6109  * @cfg {String} bgcolor Specifies the background color for a table
6110  * @cfg {Number} border Specifies whether the table cells should have borders or not
6111  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6112  * @cfg {Number} cellspacing Specifies the space between cells
6113  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6114  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6115  * @cfg {String} sortable Specifies that the table should be sortable
6116  * @cfg {String} summary Specifies a summary of the content of a table
6117  * @cfg {Number} width Specifies the width of a table
6118  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6119  * 
6120  * @cfg {boolean} striped Should the rows be alternative striped
6121  * @cfg {boolean} bordered Add borders to the table
6122  * @cfg {boolean} hover Add hover highlighting
6123  * @cfg {boolean} condensed Format condensed
6124  * @cfg {boolean} responsive Format condensed
6125  * @cfg {Boolean} loadMask (true|false) default false
6126  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6127  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6128  * @cfg {Boolean} rowSelection (true|false) default false
6129  * @cfg {Boolean} cellSelection (true|false) default false
6130  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6131  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6132  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6133  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6134  
6135  * 
6136  * @constructor
6137  * Create a new Table
6138  * @param {Object} config The config object
6139  */
6140
6141 Roo.bootstrap.Table = function(config){
6142     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6143     
6144   
6145     
6146     // BC...
6147     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6148     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6149     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6150     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6151     
6152     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6153     if (this.sm) {
6154         this.sm.grid = this;
6155         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6156         this.sm = this.selModel;
6157         this.sm.xmodule = this.xmodule || false;
6158     }
6159     
6160     if (this.cm && typeof(this.cm.config) == 'undefined') {
6161         this.colModel = new Roo.grid.ColumnModel(this.cm);
6162         this.cm = this.colModel;
6163         this.cm.xmodule = this.xmodule || false;
6164     }
6165     if (this.store) {
6166         this.store= Roo.factory(this.store, Roo.data);
6167         this.ds = this.store;
6168         this.ds.xmodule = this.xmodule || false;
6169          
6170     }
6171     if (this.footer && this.store) {
6172         this.footer.dataSource = this.ds;
6173         this.footer = Roo.factory(this.footer);
6174     }
6175     
6176     /** @private */
6177     this.addEvents({
6178         /**
6179          * @event cellclick
6180          * Fires when a cell is clicked
6181          * @param {Roo.bootstrap.Table} this
6182          * @param {Roo.Element} el
6183          * @param {Number} rowIndex
6184          * @param {Number} columnIndex
6185          * @param {Roo.EventObject} e
6186          */
6187         "cellclick" : true,
6188         /**
6189          * @event celldblclick
6190          * Fires when a cell is double clicked
6191          * @param {Roo.bootstrap.Table} this
6192          * @param {Roo.Element} el
6193          * @param {Number} rowIndex
6194          * @param {Number} columnIndex
6195          * @param {Roo.EventObject} e
6196          */
6197         "celldblclick" : true,
6198         /**
6199          * @event rowclick
6200          * Fires when a row is clicked
6201          * @param {Roo.bootstrap.Table} this
6202          * @param {Roo.Element} el
6203          * @param {Number} rowIndex
6204          * @param {Roo.EventObject} e
6205          */
6206         "rowclick" : true,
6207         /**
6208          * @event rowdblclick
6209          * Fires when a row is double clicked
6210          * @param {Roo.bootstrap.Table} this
6211          * @param {Roo.Element} el
6212          * @param {Number} rowIndex
6213          * @param {Roo.EventObject} e
6214          */
6215         "rowdblclick" : true,
6216         /**
6217          * @event mouseover
6218          * Fires when a mouseover occur
6219          * @param {Roo.bootstrap.Table} this
6220          * @param {Roo.Element} el
6221          * @param {Number} rowIndex
6222          * @param {Number} columnIndex
6223          * @param {Roo.EventObject} e
6224          */
6225         "mouseover" : true,
6226         /**
6227          * @event mouseout
6228          * Fires when a mouseout occur
6229          * @param {Roo.bootstrap.Table} this
6230          * @param {Roo.Element} el
6231          * @param {Number} rowIndex
6232          * @param {Number} columnIndex
6233          * @param {Roo.EventObject} e
6234          */
6235         "mouseout" : true,
6236         /**
6237          * @event rowclass
6238          * Fires when a row is rendered, so you can change add a style to it.
6239          * @param {Roo.bootstrap.Table} this
6240          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6241          */
6242         'rowclass' : true,
6243           /**
6244          * @event rowsrendered
6245          * Fires when all the  rows have been rendered
6246          * @param {Roo.bootstrap.Table} this
6247          */
6248         'rowsrendered' : true,
6249         /**
6250          * @event contextmenu
6251          * The raw contextmenu event for the entire grid.
6252          * @param {Roo.EventObject} e
6253          */
6254         "contextmenu" : true,
6255         /**
6256          * @event rowcontextmenu
6257          * Fires when a row is right clicked
6258          * @param {Roo.bootstrap.Table} this
6259          * @param {Number} rowIndex
6260          * @param {Roo.EventObject} e
6261          */
6262         "rowcontextmenu" : true,
6263         /**
6264          * @event cellcontextmenu
6265          * Fires when a cell is right clicked
6266          * @param {Roo.bootstrap.Table} this
6267          * @param {Number} rowIndex
6268          * @param {Number} cellIndex
6269          * @param {Roo.EventObject} e
6270          */
6271          "cellcontextmenu" : true,
6272          /**
6273          * @event headercontextmenu
6274          * Fires when a header is right clicked
6275          * @param {Roo.bootstrap.Table} this
6276          * @param {Number} columnIndex
6277          * @param {Roo.EventObject} e
6278          */
6279         "headercontextmenu" : true
6280     });
6281 };
6282
6283 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6284     
6285     cls: false,
6286     align: false,
6287     bgcolor: false,
6288     border: false,
6289     cellpadding: false,
6290     cellspacing: false,
6291     frame: false,
6292     rules: false,
6293     sortable: false,
6294     summary: false,
6295     width: false,
6296     striped : false,
6297     scrollBody : false,
6298     bordered: false,
6299     hover:  false,
6300     condensed : false,
6301     responsive : false,
6302     sm : false,
6303     cm : false,
6304     store : false,
6305     loadMask : false,
6306     footerShow : true,
6307     headerShow : true,
6308   
6309     rowSelection : false,
6310     cellSelection : false,
6311     layout : false,
6312     
6313     // Roo.Element - the tbody
6314     mainBody: false,
6315     // Roo.Element - thead element
6316     mainHead: false,
6317     
6318     container: false, // used by gridpanel...
6319     
6320     lazyLoad : false,
6321     
6322     CSS : Roo.util.CSS,
6323     
6324     auto_hide_footer : false,
6325     
6326     getAutoCreate : function()
6327     {
6328         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6329         
6330         cfg = {
6331             tag: 'table',
6332             cls : 'table',
6333             cn : []
6334         };
6335         if (this.scrollBody) {
6336             cfg.cls += ' table-body-fixed';
6337         }    
6338         if (this.striped) {
6339             cfg.cls += ' table-striped';
6340         }
6341         
6342         if (this.hover) {
6343             cfg.cls += ' table-hover';
6344         }
6345         if (this.bordered) {
6346             cfg.cls += ' table-bordered';
6347         }
6348         if (this.condensed) {
6349             cfg.cls += ' table-condensed';
6350         }
6351         if (this.responsive) {
6352             cfg.cls += ' table-responsive';
6353         }
6354         
6355         if (this.cls) {
6356             cfg.cls+=  ' ' +this.cls;
6357         }
6358         
6359         // this lot should be simplifed...
6360         var _t = this;
6361         var cp = [
6362             'align',
6363             'bgcolor',
6364             'border',
6365             'cellpadding',
6366             'cellspacing',
6367             'frame',
6368             'rules',
6369             'sortable',
6370             'summary',
6371             'width'
6372         ].forEach(function(k) {
6373             if (_t[k]) {
6374                 cfg[k] = _t[k];
6375             }
6376         });
6377         
6378         
6379         if (this.layout) {
6380             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6381         }
6382         
6383         if(this.store || this.cm){
6384             if(this.headerShow){
6385                 cfg.cn.push(this.renderHeader());
6386             }
6387             
6388             cfg.cn.push(this.renderBody());
6389             
6390             if(this.footerShow){
6391                 cfg.cn.push(this.renderFooter());
6392             }
6393             // where does this come from?
6394             //cfg.cls+=  ' TableGrid';
6395         }
6396         
6397         return { cn : [ cfg ] };
6398     },
6399     
6400     initEvents : function()
6401     {   
6402         if(!this.store || !this.cm){
6403             return;
6404         }
6405         if (this.selModel) {
6406             this.selModel.initEvents();
6407         }
6408         
6409         
6410         //Roo.log('initEvents with ds!!!!');
6411         
6412         this.mainBody = this.el.select('tbody', true).first();
6413         this.mainHead = this.el.select('thead', true).first();
6414         this.mainFoot = this.el.select('tfoot', true).first();
6415         
6416         
6417         
6418         var _this = this;
6419         
6420         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6421             e.on('click', _this.sort, _this);
6422         });
6423         
6424         this.mainBody.on("click", this.onClick, this);
6425         this.mainBody.on("dblclick", this.onDblClick, this);
6426         
6427         // why is this done????? = it breaks dialogs??
6428         //this.parent().el.setStyle('position', 'relative');
6429         
6430         
6431         if (this.footer) {
6432             this.footer.parentId = this.id;
6433             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6434             
6435             if(this.lazyLoad){
6436                 this.el.select('tfoot tr td').first().addClass('hide');
6437             }
6438         } 
6439         
6440         if(this.loadMask) {
6441             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6442         }
6443         
6444         this.store.on('load', this.onLoad, this);
6445         this.store.on('beforeload', this.onBeforeLoad, this);
6446         this.store.on('update', this.onUpdate, this);
6447         this.store.on('add', this.onAdd, this);
6448         this.store.on("clear", this.clear, this);
6449         
6450         this.el.on("contextmenu", this.onContextMenu, this);
6451         
6452         this.mainBody.on('scroll', this.onBodyScroll, this);
6453         
6454         this.cm.on("headerchange", this.onHeaderChange, this);
6455         
6456         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6457         
6458     },
6459     
6460     onContextMenu : function(e, t)
6461     {
6462         this.processEvent("contextmenu", e);
6463     },
6464     
6465     processEvent : function(name, e)
6466     {
6467         if (name != 'touchstart' ) {
6468             this.fireEvent(name, e);    
6469         }
6470         
6471         var t = e.getTarget();
6472         
6473         var cell = Roo.get(t);
6474         
6475         if(!cell){
6476             return;
6477         }
6478         
6479         if(cell.findParent('tfoot', false, true)){
6480             return;
6481         }
6482         
6483         if(cell.findParent('thead', false, true)){
6484             
6485             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6486                 cell = Roo.get(t).findParent('th', false, true);
6487                 if (!cell) {
6488                     Roo.log("failed to find th in thead?");
6489                     Roo.log(e.getTarget());
6490                     return;
6491                 }
6492             }
6493             
6494             var cellIndex = cell.dom.cellIndex;
6495             
6496             var ename = name == 'touchstart' ? 'click' : name;
6497             this.fireEvent("header" + ename, this, cellIndex, e);
6498             
6499             return;
6500         }
6501         
6502         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6503             cell = Roo.get(t).findParent('td', false, true);
6504             if (!cell) {
6505                 Roo.log("failed to find th in tbody?");
6506                 Roo.log(e.getTarget());
6507                 return;
6508             }
6509         }
6510         
6511         var row = cell.findParent('tr', false, true);
6512         var cellIndex = cell.dom.cellIndex;
6513         var rowIndex = row.dom.rowIndex - 1;
6514         
6515         if(row !== false){
6516             
6517             this.fireEvent("row" + name, this, rowIndex, e);
6518             
6519             if(cell !== false){
6520             
6521                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6522             }
6523         }
6524         
6525     },
6526     
6527     onMouseover : function(e, el)
6528     {
6529         var cell = Roo.get(el);
6530         
6531         if(!cell){
6532             return;
6533         }
6534         
6535         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6536             cell = cell.findParent('td', false, true);
6537         }
6538         
6539         var row = cell.findParent('tr', false, true);
6540         var cellIndex = cell.dom.cellIndex;
6541         var rowIndex = row.dom.rowIndex - 1; // start from 0
6542         
6543         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6544         
6545     },
6546     
6547     onMouseout : function(e, el)
6548     {
6549         var cell = Roo.get(el);
6550         
6551         if(!cell){
6552             return;
6553         }
6554         
6555         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6556             cell = cell.findParent('td', false, true);
6557         }
6558         
6559         var row = cell.findParent('tr', false, true);
6560         var cellIndex = cell.dom.cellIndex;
6561         var rowIndex = row.dom.rowIndex - 1; // start from 0
6562         
6563         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6564         
6565     },
6566     
6567     onClick : function(e, el)
6568     {
6569         var cell = Roo.get(el);
6570         
6571         if(!cell || (!this.cellSelection && !this.rowSelection)){
6572             return;
6573         }
6574         
6575         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6576             cell = cell.findParent('td', false, true);
6577         }
6578         
6579         if(!cell || typeof(cell) == 'undefined'){
6580             return;
6581         }
6582         
6583         var row = cell.findParent('tr', false, true);
6584         
6585         if(!row || typeof(row) == 'undefined'){
6586             return;
6587         }
6588         
6589         var cellIndex = cell.dom.cellIndex;
6590         var rowIndex = this.getRowIndex(row);
6591         
6592         // why??? - should these not be based on SelectionModel?
6593         if(this.cellSelection){
6594             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6595         }
6596         
6597         if(this.rowSelection){
6598             this.fireEvent('rowclick', this, row, rowIndex, e);
6599         }
6600         
6601         
6602     },
6603         
6604     onDblClick : function(e,el)
6605     {
6606         var cell = Roo.get(el);
6607         
6608         if(!cell || (!this.cellSelection && !this.rowSelection)){
6609             return;
6610         }
6611         
6612         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6613             cell = cell.findParent('td', false, true);
6614         }
6615         
6616         if(!cell || typeof(cell) == 'undefined'){
6617             return;
6618         }
6619         
6620         var row = cell.findParent('tr', false, true);
6621         
6622         if(!row || typeof(row) == 'undefined'){
6623             return;
6624         }
6625         
6626         var cellIndex = cell.dom.cellIndex;
6627         var rowIndex = this.getRowIndex(row);
6628         
6629         if(this.cellSelection){
6630             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6631         }
6632         
6633         if(this.rowSelection){
6634             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6635         }
6636     },
6637     
6638     sort : function(e,el)
6639     {
6640         var col = Roo.get(el);
6641         
6642         if(!col.hasClass('sortable')){
6643             return;
6644         }
6645         
6646         var sort = col.attr('sort');
6647         var dir = 'ASC';
6648         
6649         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6650             dir = 'DESC';
6651         }
6652         
6653         this.store.sortInfo = {field : sort, direction : dir};
6654         
6655         if (this.footer) {
6656             Roo.log("calling footer first");
6657             this.footer.onClick('first');
6658         } else {
6659         
6660             this.store.load({ params : { start : 0 } });
6661         }
6662     },
6663     
6664     renderHeader : function()
6665     {
6666         var header = {
6667             tag: 'thead',
6668             cn : []
6669         };
6670         
6671         var cm = this.cm;
6672         this.totalWidth = 0;
6673         
6674         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6675             
6676             var config = cm.config[i];
6677             
6678             var c = {
6679                 tag: 'th',
6680                 cls : 'x-hcol-' + i,
6681                 style : '',
6682                 html: cm.getColumnHeader(i)
6683             };
6684             
6685             var hh = '';
6686             
6687             if(typeof(config.sortable) != 'undefined' && config.sortable){
6688                 c.cls = 'sortable';
6689                 c.html = '<i class="glyphicon"></i>' + c.html;
6690             }
6691             
6692             if(typeof(config.lgHeader) != 'undefined'){
6693                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6694             }
6695             
6696             if(typeof(config.mdHeader) != 'undefined'){
6697                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6698             }
6699             
6700             if(typeof(config.smHeader) != 'undefined'){
6701                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6702             }
6703             
6704             if(typeof(config.xsHeader) != 'undefined'){
6705                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6706             }
6707             
6708             if(hh.length){
6709                 c.html = hh;
6710             }
6711             
6712             if(typeof(config.tooltip) != 'undefined'){
6713                 c.tooltip = config.tooltip;
6714             }
6715             
6716             if(typeof(config.colspan) != 'undefined'){
6717                 c.colspan = config.colspan;
6718             }
6719             
6720             if(typeof(config.hidden) != 'undefined' && config.hidden){
6721                 c.style += ' display:none;';
6722             }
6723             
6724             if(typeof(config.dataIndex) != 'undefined'){
6725                 c.sort = config.dataIndex;
6726             }
6727             
6728            
6729             
6730             if(typeof(config.align) != 'undefined' && config.align.length){
6731                 c.style += ' text-align:' + config.align + ';';
6732             }
6733             
6734             if(typeof(config.width) != 'undefined'){
6735                 c.style += ' width:' + config.width + 'px;';
6736                 this.totalWidth += config.width;
6737             } else {
6738                 this.totalWidth += 100; // assume minimum of 100 per column?
6739             }
6740             
6741             if(typeof(config.cls) != 'undefined'){
6742                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6743             }
6744             
6745             ['xs','sm','md','lg'].map(function(size){
6746                 
6747                 if(typeof(config[size]) == 'undefined'){
6748                     return;
6749                 }
6750                 
6751                 if (!config[size]) { // 0 = hidden
6752                     c.cls += ' hidden-' + size;
6753                     return;
6754                 }
6755                 
6756                 c.cls += ' col-' + size + '-' + config[size];
6757
6758             });
6759             
6760             header.cn.push(c)
6761         }
6762         
6763         return header;
6764     },
6765     
6766     renderBody : function()
6767     {
6768         var body = {
6769             tag: 'tbody',
6770             cn : [
6771                 {
6772                     tag: 'tr',
6773                     cn : [
6774                         {
6775                             tag : 'td',
6776                             colspan :  this.cm.getColumnCount()
6777                         }
6778                     ]
6779                 }
6780             ]
6781         };
6782         
6783         return body;
6784     },
6785     
6786     renderFooter : function()
6787     {
6788         var footer = {
6789             tag: 'tfoot',
6790             cn : [
6791                 {
6792                     tag: 'tr',
6793                     cn : [
6794                         {
6795                             tag : 'td',
6796                             colspan :  this.cm.getColumnCount()
6797                         }
6798                     ]
6799                 }
6800             ]
6801         };
6802         
6803         return footer;
6804     },
6805     
6806     
6807     
6808     onLoad : function()
6809     {
6810 //        Roo.log('ds onload');
6811         this.clear();
6812         
6813         var _this = this;
6814         var cm = this.cm;
6815         var ds = this.store;
6816         
6817         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6818             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6819             if (_this.store.sortInfo) {
6820                     
6821                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6822                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6823                 }
6824                 
6825                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6826                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6827                 }
6828             }
6829         });
6830         
6831         var tbody =  this.mainBody;
6832               
6833         if(ds.getCount() > 0){
6834             ds.data.each(function(d,rowIndex){
6835                 var row =  this.renderRow(cm, ds, rowIndex);
6836                 
6837                 tbody.createChild(row);
6838                 
6839                 var _this = this;
6840                 
6841                 if(row.cellObjects.length){
6842                     Roo.each(row.cellObjects, function(r){
6843                         _this.renderCellObject(r);
6844                     })
6845                 }
6846                 
6847             }, this);
6848         }
6849         
6850         var tfoot = this.el.select('tfoot', true).first();
6851         
6852         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6853             
6854             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6855             
6856             var total = this.ds.getTotalCount();
6857             
6858             if(this.footer.pageSize < total){
6859                 this.mainFoot.show();
6860             }
6861         }
6862         
6863         Roo.each(this.el.select('tbody td', true).elements, function(e){
6864             e.on('mouseover', _this.onMouseover, _this);
6865         });
6866         
6867         Roo.each(this.el.select('tbody td', true).elements, function(e){
6868             e.on('mouseout', _this.onMouseout, _this);
6869         });
6870         this.fireEvent('rowsrendered', this);
6871         
6872         this.autoSize();
6873     },
6874     
6875     
6876     onUpdate : function(ds,record)
6877     {
6878         this.refreshRow(record);
6879         this.autoSize();
6880     },
6881     
6882     onRemove : function(ds, record, index, isUpdate){
6883         if(isUpdate !== true){
6884             this.fireEvent("beforerowremoved", this, index, record);
6885         }
6886         var bt = this.mainBody.dom;
6887         
6888         var rows = this.el.select('tbody > tr', true).elements;
6889         
6890         if(typeof(rows[index]) != 'undefined'){
6891             bt.removeChild(rows[index].dom);
6892         }
6893         
6894 //        if(bt.rows[index]){
6895 //            bt.removeChild(bt.rows[index]);
6896 //        }
6897         
6898         if(isUpdate !== true){
6899             //this.stripeRows(index);
6900             //this.syncRowHeights(index, index);
6901             //this.layout();
6902             this.fireEvent("rowremoved", this, index, record);
6903         }
6904     },
6905     
6906     onAdd : function(ds, records, rowIndex)
6907     {
6908         //Roo.log('on Add called');
6909         // - note this does not handle multiple adding very well..
6910         var bt = this.mainBody.dom;
6911         for (var i =0 ; i < records.length;i++) {
6912             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6913             //Roo.log(records[i]);
6914             //Roo.log(this.store.getAt(rowIndex+i));
6915             this.insertRow(this.store, rowIndex + i, false);
6916             return;
6917         }
6918         
6919     },
6920     
6921     
6922     refreshRow : function(record){
6923         var ds = this.store, index;
6924         if(typeof record == 'number'){
6925             index = record;
6926             record = ds.getAt(index);
6927         }else{
6928             index = ds.indexOf(record);
6929         }
6930         this.insertRow(ds, index, true);
6931         this.autoSize();
6932         this.onRemove(ds, record, index+1, true);
6933         this.autoSize();
6934         //this.syncRowHeights(index, index);
6935         //this.layout();
6936         this.fireEvent("rowupdated", this, index, record);
6937     },
6938     
6939     insertRow : function(dm, rowIndex, isUpdate){
6940         
6941         if(!isUpdate){
6942             this.fireEvent("beforerowsinserted", this, rowIndex);
6943         }
6944             //var s = this.getScrollState();
6945         var row = this.renderRow(this.cm, this.store, rowIndex);
6946         // insert before rowIndex..
6947         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6948         
6949         var _this = this;
6950                 
6951         if(row.cellObjects.length){
6952             Roo.each(row.cellObjects, function(r){
6953                 _this.renderCellObject(r);
6954             })
6955         }
6956             
6957         if(!isUpdate){
6958             this.fireEvent("rowsinserted", this, rowIndex);
6959             //this.syncRowHeights(firstRow, lastRow);
6960             //this.stripeRows(firstRow);
6961             //this.layout();
6962         }
6963         
6964     },
6965     
6966     
6967     getRowDom : function(rowIndex)
6968     {
6969         var rows = this.el.select('tbody > tr', true).elements;
6970         
6971         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6972         
6973     },
6974     // returns the object tree for a tr..
6975   
6976     
6977     renderRow : function(cm, ds, rowIndex) 
6978     {
6979         var d = ds.getAt(rowIndex);
6980         
6981         var row = {
6982             tag : 'tr',
6983             cls : 'x-row-' + rowIndex,
6984             cn : []
6985         };
6986             
6987         var cellObjects = [];
6988         
6989         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6990             var config = cm.config[i];
6991             
6992             var renderer = cm.getRenderer(i);
6993             var value = '';
6994             var id = false;
6995             
6996             if(typeof(renderer) !== 'undefined'){
6997                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6998             }
6999             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7000             // and are rendered into the cells after the row is rendered - using the id for the element.
7001             
7002             if(typeof(value) === 'object'){
7003                 id = Roo.id();
7004                 cellObjects.push({
7005                     container : id,
7006                     cfg : value 
7007                 })
7008             }
7009             
7010             var rowcfg = {
7011                 record: d,
7012                 rowIndex : rowIndex,
7013                 colIndex : i,
7014                 rowClass : ''
7015             };
7016
7017             this.fireEvent('rowclass', this, rowcfg);
7018             
7019             var td = {
7020                 tag: 'td',
7021                 cls : rowcfg.rowClass + ' x-col-' + i,
7022                 style: '',
7023                 html: (typeof(value) === 'object') ? '' : value
7024             };
7025             
7026             if (id) {
7027                 td.id = id;
7028             }
7029             
7030             if(typeof(config.colspan) != 'undefined'){
7031                 td.colspan = config.colspan;
7032             }
7033             
7034             if(typeof(config.hidden) != 'undefined' && config.hidden){
7035                 td.style += ' display:none;';
7036             }
7037             
7038             if(typeof(config.align) != 'undefined' && config.align.length){
7039                 td.style += ' text-align:' + config.align + ';';
7040             }
7041             if(typeof(config.valign) != 'undefined' && config.valign.length){
7042                 td.style += ' vertical-align:' + config.valign + ';';
7043             }
7044             
7045             if(typeof(config.width) != 'undefined'){
7046                 td.style += ' width:' +  config.width + 'px;';
7047             }
7048             
7049             if(typeof(config.cursor) != 'undefined'){
7050                 td.style += ' cursor:' +  config.cursor + ';';
7051             }
7052             
7053             if(typeof(config.cls) != 'undefined'){
7054                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7055             }
7056             
7057             ['xs','sm','md','lg'].map(function(size){
7058                 
7059                 if(typeof(config[size]) == 'undefined'){
7060                     return;
7061                 }
7062                 
7063                 if (!config[size]) { // 0 = hidden
7064                     td.cls += ' hidden-' + size;
7065                     return;
7066                 }
7067                 
7068                 td.cls += ' col-' + size + '-' + config[size];
7069
7070             });
7071             
7072             row.cn.push(td);
7073            
7074         }
7075         
7076         row.cellObjects = cellObjects;
7077         
7078         return row;
7079           
7080     },
7081     
7082     
7083     
7084     onBeforeLoad : function()
7085     {
7086         
7087     },
7088      /**
7089      * Remove all rows
7090      */
7091     clear : function()
7092     {
7093         this.el.select('tbody', true).first().dom.innerHTML = '';
7094     },
7095     /**
7096      * Show or hide a row.
7097      * @param {Number} rowIndex to show or hide
7098      * @param {Boolean} state hide
7099      */
7100     setRowVisibility : function(rowIndex, state)
7101     {
7102         var bt = this.mainBody.dom;
7103         
7104         var rows = this.el.select('tbody > tr', true).elements;
7105         
7106         if(typeof(rows[rowIndex]) == 'undefined'){
7107             return;
7108         }
7109         rows[rowIndex].dom.style.display = state ? '' : 'none';
7110     },
7111     
7112     
7113     getSelectionModel : function(){
7114         if(!this.selModel){
7115             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7116         }
7117         return this.selModel;
7118     },
7119     /*
7120      * Render the Roo.bootstrap object from renderder
7121      */
7122     renderCellObject : function(r)
7123     {
7124         var _this = this;
7125         
7126         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7127         
7128         var t = r.cfg.render(r.container);
7129         
7130         if(r.cfg.cn){
7131             Roo.each(r.cfg.cn, function(c){
7132                 var child = {
7133                     container: t.getChildContainer(),
7134                     cfg: c
7135                 };
7136                 _this.renderCellObject(child);
7137             })
7138         }
7139     },
7140     
7141     getRowIndex : function(row)
7142     {
7143         var rowIndex = -1;
7144         
7145         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7146             if(el != row){
7147                 return;
7148             }
7149             
7150             rowIndex = index;
7151         });
7152         
7153         return rowIndex;
7154     },
7155      /**
7156      * Returns the grid's underlying element = used by panel.Grid
7157      * @return {Element} The element
7158      */
7159     getGridEl : function(){
7160         return this.el;
7161     },
7162      /**
7163      * Forces a resize - used by panel.Grid
7164      * @return {Element} The element
7165      */
7166     autoSize : function()
7167     {
7168         //var ctr = Roo.get(this.container.dom.parentElement);
7169         var ctr = Roo.get(this.el.dom);
7170         
7171         var thd = this.getGridEl().select('thead',true).first();
7172         var tbd = this.getGridEl().select('tbody', true).first();
7173         var tfd = this.getGridEl().select('tfoot', true).first();
7174         
7175         var cw = ctr.getWidth();
7176         
7177         if (tbd) {
7178             
7179             tbd.setSize(ctr.getWidth(),
7180                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7181             );
7182             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7183             cw -= barsize;
7184         }
7185         cw = Math.max(cw, this.totalWidth);
7186         this.getGridEl().select('tr',true).setWidth(cw);
7187         // resize 'expandable coloumn?
7188         
7189         return; // we doe not have a view in this design..
7190         
7191     },
7192     onBodyScroll: function()
7193     {
7194         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7195         if(this.mainHead){
7196             this.mainHead.setStyle({
7197                 'position' : 'relative',
7198                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7199             });
7200         }
7201         
7202         if(this.lazyLoad){
7203             
7204             var scrollHeight = this.mainBody.dom.scrollHeight;
7205             
7206             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7207             
7208             var height = this.mainBody.getHeight();
7209             
7210             if(scrollHeight - height == scrollTop) {
7211                 
7212                 var total = this.ds.getTotalCount();
7213                 
7214                 if(this.footer.cursor + this.footer.pageSize < total){
7215                     
7216                     this.footer.ds.load({
7217                         params : {
7218                             start : this.footer.cursor + this.footer.pageSize,
7219                             limit : this.footer.pageSize
7220                         },
7221                         add : true
7222                     });
7223                 }
7224             }
7225             
7226         }
7227     },
7228     
7229     onHeaderChange : function()
7230     {
7231         var header = this.renderHeader();
7232         var table = this.el.select('table', true).first();
7233         
7234         this.mainHead.remove();
7235         this.mainHead = table.createChild(header, this.mainBody, false);
7236     },
7237     
7238     onHiddenChange : function(colModel, colIndex, hidden)
7239     {
7240         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7241         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7242         
7243         this.CSS.updateRule(thSelector, "display", "");
7244         this.CSS.updateRule(tdSelector, "display", "");
7245         
7246         if(hidden){
7247             this.CSS.updateRule(thSelector, "display", "none");
7248             this.CSS.updateRule(tdSelector, "display", "none");
7249         }
7250         
7251         this.onHeaderChange();
7252         this.onLoad();
7253     },
7254     
7255     setColumnWidth: function(col_index, width)
7256     {
7257         // width = "md-2 xs-2..."
7258         if(!this.colModel.config[col_index]) {
7259             return;
7260         }
7261         
7262         var w = width.split(" ");
7263         
7264         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7265         
7266         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7267         
7268         
7269         for(var j = 0; j < w.length; j++) {
7270             
7271             if(!w[j]) {
7272                 continue;
7273             }
7274             
7275             var size_cls = w[j].split("-");
7276             
7277             if(!Number.isInteger(size_cls[1] * 1)) {
7278                 continue;
7279             }
7280             
7281             if(!this.colModel.config[col_index][size_cls[0]]) {
7282                 continue;
7283             }
7284             
7285             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7286                 continue;
7287             }
7288             
7289             h_row[0].classList.replace(
7290                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7291                 "col-"+size_cls[0]+"-"+size_cls[1]
7292             );
7293             
7294             for(var i = 0; i < rows.length; i++) {
7295                 
7296                 var size_cls = w[j].split("-");
7297                 
7298                 if(!Number.isInteger(size_cls[1] * 1)) {
7299                     continue;
7300                 }
7301                 
7302                 if(!this.colModel.config[col_index][size_cls[0]]) {
7303                     continue;
7304                 }
7305                 
7306                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7307                     continue;
7308                 }
7309                 
7310                 rows[i].classList.replace(
7311                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7312                     "col-"+size_cls[0]+"-"+size_cls[1]
7313                 );
7314             }
7315             
7316             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7317         }
7318     }
7319 });
7320
7321  
7322
7323  /*
7324  * - LGPL
7325  *
7326  * table cell
7327  * 
7328  */
7329
7330 /**
7331  * @class Roo.bootstrap.TableCell
7332  * @extends Roo.bootstrap.Component
7333  * Bootstrap TableCell class
7334  * @cfg {String} html cell contain text
7335  * @cfg {String} cls cell class
7336  * @cfg {String} tag cell tag (td|th) default td
7337  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7338  * @cfg {String} align Aligns the content in a cell
7339  * @cfg {String} axis Categorizes cells
7340  * @cfg {String} bgcolor Specifies the background color of a cell
7341  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7342  * @cfg {Number} colspan Specifies the number of columns a cell should span
7343  * @cfg {String} headers Specifies one or more header cells a cell is related to
7344  * @cfg {Number} height Sets the height of a cell
7345  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7346  * @cfg {Number} rowspan Sets the number of rows a cell should span
7347  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7348  * @cfg {String} valign Vertical aligns the content in a cell
7349  * @cfg {Number} width Specifies the width of a cell
7350  * 
7351  * @constructor
7352  * Create a new TableCell
7353  * @param {Object} config The config object
7354  */
7355
7356 Roo.bootstrap.TableCell = function(config){
7357     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7358 };
7359
7360 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7361     
7362     html: false,
7363     cls: false,
7364     tag: false,
7365     abbr: false,
7366     align: false,
7367     axis: false,
7368     bgcolor: false,
7369     charoff: false,
7370     colspan: false,
7371     headers: false,
7372     height: false,
7373     nowrap: false,
7374     rowspan: false,
7375     scope: false,
7376     valign: false,
7377     width: false,
7378     
7379     
7380     getAutoCreate : function(){
7381         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7382         
7383         cfg = {
7384             tag: 'td'
7385         };
7386         
7387         if(this.tag){
7388             cfg.tag = this.tag;
7389         }
7390         
7391         if (this.html) {
7392             cfg.html=this.html
7393         }
7394         if (this.cls) {
7395             cfg.cls=this.cls
7396         }
7397         if (this.abbr) {
7398             cfg.abbr=this.abbr
7399         }
7400         if (this.align) {
7401             cfg.align=this.align
7402         }
7403         if (this.axis) {
7404             cfg.axis=this.axis
7405         }
7406         if (this.bgcolor) {
7407             cfg.bgcolor=this.bgcolor
7408         }
7409         if (this.charoff) {
7410             cfg.charoff=this.charoff
7411         }
7412         if (this.colspan) {
7413             cfg.colspan=this.colspan
7414         }
7415         if (this.headers) {
7416             cfg.headers=this.headers
7417         }
7418         if (this.height) {
7419             cfg.height=this.height
7420         }
7421         if (this.nowrap) {
7422             cfg.nowrap=this.nowrap
7423         }
7424         if (this.rowspan) {
7425             cfg.rowspan=this.rowspan
7426         }
7427         if (this.scope) {
7428             cfg.scope=this.scope
7429         }
7430         if (this.valign) {
7431             cfg.valign=this.valign
7432         }
7433         if (this.width) {
7434             cfg.width=this.width
7435         }
7436         
7437         
7438         return cfg;
7439     }
7440    
7441 });
7442
7443  
7444
7445  /*
7446  * - LGPL
7447  *
7448  * table row
7449  * 
7450  */
7451
7452 /**
7453  * @class Roo.bootstrap.TableRow
7454  * @extends Roo.bootstrap.Component
7455  * Bootstrap TableRow class
7456  * @cfg {String} cls row class
7457  * @cfg {String} align Aligns the content in a table row
7458  * @cfg {String} bgcolor Specifies a background color for a table row
7459  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7460  * @cfg {String} valign Vertical aligns the content in a table row
7461  * 
7462  * @constructor
7463  * Create a new TableRow
7464  * @param {Object} config The config object
7465  */
7466
7467 Roo.bootstrap.TableRow = function(config){
7468     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7469 };
7470
7471 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7472     
7473     cls: false,
7474     align: false,
7475     bgcolor: false,
7476     charoff: false,
7477     valign: false,
7478     
7479     getAutoCreate : function(){
7480         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7481         
7482         cfg = {
7483             tag: 'tr'
7484         };
7485             
7486         if(this.cls){
7487             cfg.cls = this.cls;
7488         }
7489         if(this.align){
7490             cfg.align = this.align;
7491         }
7492         if(this.bgcolor){
7493             cfg.bgcolor = this.bgcolor;
7494         }
7495         if(this.charoff){
7496             cfg.charoff = this.charoff;
7497         }
7498         if(this.valign){
7499             cfg.valign = this.valign;
7500         }
7501         
7502         return cfg;
7503     }
7504    
7505 });
7506
7507  
7508
7509  /*
7510  * - LGPL
7511  *
7512  * table body
7513  * 
7514  */
7515
7516 /**
7517  * @class Roo.bootstrap.TableBody
7518  * @extends Roo.bootstrap.Component
7519  * Bootstrap TableBody class
7520  * @cfg {String} cls element class
7521  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7522  * @cfg {String} align Aligns the content inside the element
7523  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7524  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7525  * 
7526  * @constructor
7527  * Create a new TableBody
7528  * @param {Object} config The config object
7529  */
7530
7531 Roo.bootstrap.TableBody = function(config){
7532     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7533 };
7534
7535 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7536     
7537     cls: false,
7538     tag: false,
7539     align: false,
7540     charoff: false,
7541     valign: false,
7542     
7543     getAutoCreate : function(){
7544         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7545         
7546         cfg = {
7547             tag: 'tbody'
7548         };
7549             
7550         if (this.cls) {
7551             cfg.cls=this.cls
7552         }
7553         if(this.tag){
7554             cfg.tag = this.tag;
7555         }
7556         
7557         if(this.align){
7558             cfg.align = this.align;
7559         }
7560         if(this.charoff){
7561             cfg.charoff = this.charoff;
7562         }
7563         if(this.valign){
7564             cfg.valign = this.valign;
7565         }
7566         
7567         return cfg;
7568     }
7569     
7570     
7571 //    initEvents : function()
7572 //    {
7573 //        
7574 //        if(!this.store){
7575 //            return;
7576 //        }
7577 //        
7578 //        this.store = Roo.factory(this.store, Roo.data);
7579 //        this.store.on('load', this.onLoad, this);
7580 //        
7581 //        this.store.load();
7582 //        
7583 //    },
7584 //    
7585 //    onLoad: function () 
7586 //    {   
7587 //        this.fireEvent('load', this);
7588 //    }
7589 //    
7590 //   
7591 });
7592
7593  
7594
7595  /*
7596  * Based on:
7597  * Ext JS Library 1.1.1
7598  * Copyright(c) 2006-2007, Ext JS, LLC.
7599  *
7600  * Originally Released Under LGPL - original licence link has changed is not relivant.
7601  *
7602  * Fork - LGPL
7603  * <script type="text/javascript">
7604  */
7605
7606 // as we use this in bootstrap.
7607 Roo.namespace('Roo.form');
7608  /**
7609  * @class Roo.form.Action
7610  * Internal Class used to handle form actions
7611  * @constructor
7612  * @param {Roo.form.BasicForm} el The form element or its id
7613  * @param {Object} config Configuration options
7614  */
7615
7616  
7617  
7618 // define the action interface
7619 Roo.form.Action = function(form, options){
7620     this.form = form;
7621     this.options = options || {};
7622 };
7623 /**
7624  * Client Validation Failed
7625  * @const 
7626  */
7627 Roo.form.Action.CLIENT_INVALID = 'client';
7628 /**
7629  * Server Validation Failed
7630  * @const 
7631  */
7632 Roo.form.Action.SERVER_INVALID = 'server';
7633  /**
7634  * Connect to Server Failed
7635  * @const 
7636  */
7637 Roo.form.Action.CONNECT_FAILURE = 'connect';
7638 /**
7639  * Reading Data from Server Failed
7640  * @const 
7641  */
7642 Roo.form.Action.LOAD_FAILURE = 'load';
7643
7644 Roo.form.Action.prototype = {
7645     type : 'default',
7646     failureType : undefined,
7647     response : undefined,
7648     result : undefined,
7649
7650     // interface method
7651     run : function(options){
7652
7653     },
7654
7655     // interface method
7656     success : function(response){
7657
7658     },
7659
7660     // interface method
7661     handleResponse : function(response){
7662
7663     },
7664
7665     // default connection failure
7666     failure : function(response){
7667         
7668         this.response = response;
7669         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7670         this.form.afterAction(this, false);
7671     },
7672
7673     processResponse : function(response){
7674         this.response = response;
7675         if(!response.responseText){
7676             return true;
7677         }
7678         this.result = this.handleResponse(response);
7679         return this.result;
7680     },
7681
7682     // utility functions used internally
7683     getUrl : function(appendParams){
7684         var url = this.options.url || this.form.url || this.form.el.dom.action;
7685         if(appendParams){
7686             var p = this.getParams();
7687             if(p){
7688                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7689             }
7690         }
7691         return url;
7692     },
7693
7694     getMethod : function(){
7695         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7696     },
7697
7698     getParams : function(){
7699         var bp = this.form.baseParams;
7700         var p = this.options.params;
7701         if(p){
7702             if(typeof p == "object"){
7703                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7704             }else if(typeof p == 'string' && bp){
7705                 p += '&' + Roo.urlEncode(bp);
7706             }
7707         }else if(bp){
7708             p = Roo.urlEncode(bp);
7709         }
7710         return p;
7711     },
7712
7713     createCallback : function(){
7714         return {
7715             success: this.success,
7716             failure: this.failure,
7717             scope: this,
7718             timeout: (this.form.timeout*1000),
7719             upload: this.form.fileUpload ? this.success : undefined
7720         };
7721     }
7722 };
7723
7724 Roo.form.Action.Submit = function(form, options){
7725     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7726 };
7727
7728 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7729     type : 'submit',
7730
7731     haveProgress : false,
7732     uploadComplete : false,
7733     
7734     // uploadProgress indicator.
7735     uploadProgress : function()
7736     {
7737         if (!this.form.progressUrl) {
7738             return;
7739         }
7740         
7741         if (!this.haveProgress) {
7742             Roo.MessageBox.progress("Uploading", "Uploading");
7743         }
7744         if (this.uploadComplete) {
7745            Roo.MessageBox.hide();
7746            return;
7747         }
7748         
7749         this.haveProgress = true;
7750    
7751         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7752         
7753         var c = new Roo.data.Connection();
7754         c.request({
7755             url : this.form.progressUrl,
7756             params: {
7757                 id : uid
7758             },
7759             method: 'GET',
7760             success : function(req){
7761                //console.log(data);
7762                 var rdata = false;
7763                 var edata;
7764                 try  {
7765                    rdata = Roo.decode(req.responseText)
7766                 } catch (e) {
7767                     Roo.log("Invalid data from server..");
7768                     Roo.log(edata);
7769                     return;
7770                 }
7771                 if (!rdata || !rdata.success) {
7772                     Roo.log(rdata);
7773                     Roo.MessageBox.alert(Roo.encode(rdata));
7774                     return;
7775                 }
7776                 var data = rdata.data;
7777                 
7778                 if (this.uploadComplete) {
7779                    Roo.MessageBox.hide();
7780                    return;
7781                 }
7782                    
7783                 if (data){
7784                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7785                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7786                     );
7787                 }
7788                 this.uploadProgress.defer(2000,this);
7789             },
7790        
7791             failure: function(data) {
7792                 Roo.log('progress url failed ');
7793                 Roo.log(data);
7794             },
7795             scope : this
7796         });
7797            
7798     },
7799     
7800     
7801     run : function()
7802     {
7803         // run get Values on the form, so it syncs any secondary forms.
7804         this.form.getValues();
7805         
7806         var o = this.options;
7807         var method = this.getMethod();
7808         var isPost = method == 'POST';
7809         if(o.clientValidation === false || this.form.isValid()){
7810             
7811             if (this.form.progressUrl) {
7812                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7813                     (new Date() * 1) + '' + Math.random());
7814                     
7815             } 
7816             
7817             
7818             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7819                 form:this.form.el.dom,
7820                 url:this.getUrl(!isPost),
7821                 method: method,
7822                 params:isPost ? this.getParams() : null,
7823                 isUpload: this.form.fileUpload
7824             }));
7825             
7826             this.uploadProgress();
7827
7828         }else if (o.clientValidation !== false){ // client validation failed
7829             this.failureType = Roo.form.Action.CLIENT_INVALID;
7830             this.form.afterAction(this, false);
7831         }
7832     },
7833
7834     success : function(response)
7835     {
7836         this.uploadComplete= true;
7837         if (this.haveProgress) {
7838             Roo.MessageBox.hide();
7839         }
7840         
7841         
7842         var result = this.processResponse(response);
7843         if(result === true || result.success){
7844             this.form.afterAction(this, true);
7845             return;
7846         }
7847         if(result.errors){
7848             this.form.markInvalid(result.errors);
7849             this.failureType = Roo.form.Action.SERVER_INVALID;
7850         }
7851         this.form.afterAction(this, false);
7852     },
7853     failure : function(response)
7854     {
7855         this.uploadComplete= true;
7856         if (this.haveProgress) {
7857             Roo.MessageBox.hide();
7858         }
7859         
7860         this.response = response;
7861         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7862         this.form.afterAction(this, false);
7863     },
7864     
7865     handleResponse : function(response){
7866         if(this.form.errorReader){
7867             var rs = this.form.errorReader.read(response);
7868             var errors = [];
7869             if(rs.records){
7870                 for(var i = 0, len = rs.records.length; i < len; i++) {
7871                     var r = rs.records[i];
7872                     errors[i] = r.data;
7873                 }
7874             }
7875             if(errors.length < 1){
7876                 errors = null;
7877             }
7878             return {
7879                 success : rs.success,
7880                 errors : errors
7881             };
7882         }
7883         var ret = false;
7884         try {
7885             ret = Roo.decode(response.responseText);
7886         } catch (e) {
7887             ret = {
7888                 success: false,
7889                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7890                 errors : []
7891             };
7892         }
7893         return ret;
7894         
7895     }
7896 });
7897
7898
7899 Roo.form.Action.Load = function(form, options){
7900     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7901     this.reader = this.form.reader;
7902 };
7903
7904 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7905     type : 'load',
7906
7907     run : function(){
7908         
7909         Roo.Ajax.request(Roo.apply(
7910                 this.createCallback(), {
7911                     method:this.getMethod(),
7912                     url:this.getUrl(false),
7913                     params:this.getParams()
7914         }));
7915     },
7916
7917     success : function(response){
7918         
7919         var result = this.processResponse(response);
7920         if(result === true || !result.success || !result.data){
7921             this.failureType = Roo.form.Action.LOAD_FAILURE;
7922             this.form.afterAction(this, false);
7923             return;
7924         }
7925         this.form.clearInvalid();
7926         this.form.setValues(result.data);
7927         this.form.afterAction(this, true);
7928     },
7929
7930     handleResponse : function(response){
7931         if(this.form.reader){
7932             var rs = this.form.reader.read(response);
7933             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7934             return {
7935                 success : rs.success,
7936                 data : data
7937             };
7938         }
7939         return Roo.decode(response.responseText);
7940     }
7941 });
7942
7943 Roo.form.Action.ACTION_TYPES = {
7944     'load' : Roo.form.Action.Load,
7945     'submit' : Roo.form.Action.Submit
7946 };/*
7947  * - LGPL
7948  *
7949  * form
7950  *
7951  */
7952
7953 /**
7954  * @class Roo.bootstrap.Form
7955  * @extends Roo.bootstrap.Component
7956  * Bootstrap Form class
7957  * @cfg {String} method  GET | POST (default POST)
7958  * @cfg {String} labelAlign top | left (default top)
7959  * @cfg {String} align left  | right - for navbars
7960  * @cfg {Boolean} loadMask load mask when submit (default true)
7961
7962  *
7963  * @constructor
7964  * Create a new Form
7965  * @param {Object} config The config object
7966  */
7967
7968
7969 Roo.bootstrap.Form = function(config){
7970     
7971     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7972     
7973     Roo.bootstrap.Form.popover.apply();
7974     
7975     this.addEvents({
7976         /**
7977          * @event clientvalidation
7978          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7979          * @param {Form} this
7980          * @param {Boolean} valid true if the form has passed client-side validation
7981          */
7982         clientvalidation: true,
7983         /**
7984          * @event beforeaction
7985          * Fires before any action is performed. Return false to cancel the action.
7986          * @param {Form} this
7987          * @param {Action} action The action to be performed
7988          */
7989         beforeaction: true,
7990         /**
7991          * @event actionfailed
7992          * Fires when an action fails.
7993          * @param {Form} this
7994          * @param {Action} action The action that failed
7995          */
7996         actionfailed : true,
7997         /**
7998          * @event actioncomplete
7999          * Fires when an action is completed.
8000          * @param {Form} this
8001          * @param {Action} action The action that completed
8002          */
8003         actioncomplete : true
8004     });
8005 };
8006
8007 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8008
8009      /**
8010      * @cfg {String} method
8011      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8012      */
8013     method : 'POST',
8014     /**
8015      * @cfg {String} url
8016      * The URL to use for form actions if one isn't supplied in the action options.
8017      */
8018     /**
8019      * @cfg {Boolean} fileUpload
8020      * Set to true if this form is a file upload.
8021      */
8022
8023     /**
8024      * @cfg {Object} baseParams
8025      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8026      */
8027
8028     /**
8029      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8030      */
8031     timeout: 30,
8032     /**
8033      * @cfg {Sting} align (left|right) for navbar forms
8034      */
8035     align : 'left',
8036
8037     // private
8038     activeAction : null,
8039
8040     /**
8041      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8042      * element by passing it or its id or mask the form itself by passing in true.
8043      * @type Mixed
8044      */
8045     waitMsgTarget : false,
8046
8047     loadMask : true,
8048     
8049     /**
8050      * @cfg {Boolean} errorMask (true|false) default false
8051      */
8052     errorMask : false,
8053     
8054     /**
8055      * @cfg {Number} maskOffset Default 100
8056      */
8057     maskOffset : 100,
8058     
8059     /**
8060      * @cfg {Boolean} maskBody
8061      */
8062     maskBody : false,
8063
8064     getAutoCreate : function(){
8065
8066         var cfg = {
8067             tag: 'form',
8068             method : this.method || 'POST',
8069             id : this.id || Roo.id(),
8070             cls : ''
8071         };
8072         if (this.parent().xtype.match(/^Nav/)) {
8073             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8074
8075         }
8076
8077         if (this.labelAlign == 'left' ) {
8078             cfg.cls += ' form-horizontal';
8079         }
8080
8081
8082         return cfg;
8083     },
8084     initEvents : function()
8085     {
8086         this.el.on('submit', this.onSubmit, this);
8087         // this was added as random key presses on the form where triggering form submit.
8088         this.el.on('keypress', function(e) {
8089             if (e.getCharCode() != 13) {
8090                 return true;
8091             }
8092             // we might need to allow it for textareas.. and some other items.
8093             // check e.getTarget().
8094
8095             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8096                 return true;
8097             }
8098
8099             Roo.log("keypress blocked");
8100
8101             e.preventDefault();
8102             return false;
8103         });
8104         
8105     },
8106     // private
8107     onSubmit : function(e){
8108         e.stopEvent();
8109     },
8110
8111      /**
8112      * Returns true if client-side validation on the form is successful.
8113      * @return Boolean
8114      */
8115     isValid : function(){
8116         var items = this.getItems();
8117         var valid = true;
8118         var target = false;
8119         
8120         items.each(function(f){
8121             
8122             if(f.validate()){
8123                 return;
8124             }
8125             
8126             Roo.log('invalid field: ' + f.name);
8127             
8128             valid = false;
8129
8130             if(!target && f.el.isVisible(true)){
8131                 target = f;
8132             }
8133            
8134         });
8135         
8136         if(this.errorMask && !valid){
8137             Roo.bootstrap.Form.popover.mask(this, target);
8138         }
8139         
8140         return valid;
8141     },
8142     
8143     /**
8144      * Returns true if any fields in this form have changed since their original load.
8145      * @return Boolean
8146      */
8147     isDirty : function(){
8148         var dirty = false;
8149         var items = this.getItems();
8150         items.each(function(f){
8151            if(f.isDirty()){
8152                dirty = true;
8153                return false;
8154            }
8155            return true;
8156         });
8157         return dirty;
8158     },
8159      /**
8160      * Performs a predefined action (submit or load) or custom actions you define on this form.
8161      * @param {String} actionName The name of the action type
8162      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8163      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8164      * accept other config options):
8165      * <pre>
8166 Property          Type             Description
8167 ----------------  ---------------  ----------------------------------------------------------------------------------
8168 url               String           The url for the action (defaults to the form's url)
8169 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8170 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8171 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8172                                    validate the form on the client (defaults to false)
8173      * </pre>
8174      * @return {BasicForm} this
8175      */
8176     doAction : function(action, options){
8177         if(typeof action == 'string'){
8178             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8179         }
8180         if(this.fireEvent('beforeaction', this, action) !== false){
8181             this.beforeAction(action);
8182             action.run.defer(100, action);
8183         }
8184         return this;
8185     },
8186
8187     // private
8188     beforeAction : function(action){
8189         var o = action.options;
8190         
8191         if(this.loadMask){
8192             
8193             if(this.maskBody){
8194                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8195             } else {
8196                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8197             }
8198         }
8199         // not really supported yet.. ??
8200
8201         //if(this.waitMsgTarget === true){
8202         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8203         //}else if(this.waitMsgTarget){
8204         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8205         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8206         //}else {
8207         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8208        // }
8209
8210     },
8211
8212     // private
8213     afterAction : function(action, success){
8214         this.activeAction = null;
8215         var o = action.options;
8216
8217         if(this.loadMask){
8218             
8219             if(this.maskBody){
8220                 Roo.get(document.body).unmask();
8221             } else {
8222                 this.el.unmask();
8223             }
8224         }
8225         
8226         //if(this.waitMsgTarget === true){
8227 //            this.el.unmask();
8228         //}else if(this.waitMsgTarget){
8229         //    this.waitMsgTarget.unmask();
8230         //}else{
8231         //    Roo.MessageBox.updateProgress(1);
8232         //    Roo.MessageBox.hide();
8233        // }
8234         //
8235         if(success){
8236             if(o.reset){
8237                 this.reset();
8238             }
8239             Roo.callback(o.success, o.scope, [this, action]);
8240             this.fireEvent('actioncomplete', this, action);
8241
8242         }else{
8243
8244             // failure condition..
8245             // we have a scenario where updates need confirming.
8246             // eg. if a locking scenario exists..
8247             // we look for { errors : { needs_confirm : true }} in the response.
8248             if (
8249                 (typeof(action.result) != 'undefined')  &&
8250                 (typeof(action.result.errors) != 'undefined')  &&
8251                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8252            ){
8253                 var _t = this;
8254                 Roo.log("not supported yet");
8255                  /*
8256
8257                 Roo.MessageBox.confirm(
8258                     "Change requires confirmation",
8259                     action.result.errorMsg,
8260                     function(r) {
8261                         if (r != 'yes') {
8262                             return;
8263                         }
8264                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8265                     }
8266
8267                 );
8268                 */
8269
8270
8271                 return;
8272             }
8273
8274             Roo.callback(o.failure, o.scope, [this, action]);
8275             // show an error message if no failed handler is set..
8276             if (!this.hasListener('actionfailed')) {
8277                 Roo.log("need to add dialog support");
8278                 /*
8279                 Roo.MessageBox.alert("Error",
8280                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8281                         action.result.errorMsg :
8282                         "Saving Failed, please check your entries or try again"
8283                 );
8284                 */
8285             }
8286
8287             this.fireEvent('actionfailed', this, action);
8288         }
8289
8290     },
8291     /**
8292      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8293      * @param {String} id The value to search for
8294      * @return Field
8295      */
8296     findField : function(id){
8297         var items = this.getItems();
8298         var field = items.get(id);
8299         if(!field){
8300              items.each(function(f){
8301                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8302                     field = f;
8303                     return false;
8304                 }
8305                 return true;
8306             });
8307         }
8308         return field || null;
8309     },
8310      /**
8311      * Mark fields in this form invalid in bulk.
8312      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8313      * @return {BasicForm} this
8314      */
8315     markInvalid : function(errors){
8316         if(errors instanceof Array){
8317             for(var i = 0, len = errors.length; i < len; i++){
8318                 var fieldError = errors[i];
8319                 var f = this.findField(fieldError.id);
8320                 if(f){
8321                     f.markInvalid(fieldError.msg);
8322                 }
8323             }
8324         }else{
8325             var field, id;
8326             for(id in errors){
8327                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8328                     field.markInvalid(errors[id]);
8329                 }
8330             }
8331         }
8332         //Roo.each(this.childForms || [], function (f) {
8333         //    f.markInvalid(errors);
8334         //});
8335
8336         return this;
8337     },
8338
8339     /**
8340      * Set values for fields in this form in bulk.
8341      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8342      * @return {BasicForm} this
8343      */
8344     setValues : function(values){
8345         if(values instanceof Array){ // array of objects
8346             for(var i = 0, len = values.length; i < len; i++){
8347                 var v = values[i];
8348                 var f = this.findField(v.id);
8349                 if(f){
8350                     f.setValue(v.value);
8351                     if(this.trackResetOnLoad){
8352                         f.originalValue = f.getValue();
8353                     }
8354                 }
8355             }
8356         }else{ // object hash
8357             var field, id;
8358             for(id in values){
8359                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8360
8361                     if (field.setFromData &&
8362                         field.valueField &&
8363                         field.displayField &&
8364                         // combos' with local stores can
8365                         // be queried via setValue()
8366                         // to set their value..
8367                         (field.store && !field.store.isLocal)
8368                         ) {
8369                         // it's a combo
8370                         var sd = { };
8371                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8372                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8373                         field.setFromData(sd);
8374
8375                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8376                         
8377                         field.setFromData(values);
8378                         
8379                     } else {
8380                         field.setValue(values[id]);
8381                     }
8382
8383
8384                     if(this.trackResetOnLoad){
8385                         field.originalValue = field.getValue();
8386                     }
8387                 }
8388             }
8389         }
8390
8391         //Roo.each(this.childForms || [], function (f) {
8392         //    f.setValues(values);
8393         //});
8394
8395         return this;
8396     },
8397
8398     /**
8399      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8400      * they are returned as an array.
8401      * @param {Boolean} asString
8402      * @return {Object}
8403      */
8404     getValues : function(asString){
8405         //if (this.childForms) {
8406             // copy values from the child forms
8407         //    Roo.each(this.childForms, function (f) {
8408         //        this.setValues(f.getValues());
8409         //    }, this);
8410         //}
8411
8412
8413
8414         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8415         if(asString === true){
8416             return fs;
8417         }
8418         return Roo.urlDecode(fs);
8419     },
8420
8421     /**
8422      * Returns the fields in this form as an object with key/value pairs.
8423      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8424      * @return {Object}
8425      */
8426     getFieldValues : function(with_hidden)
8427     {
8428         var items = this.getItems();
8429         var ret = {};
8430         items.each(function(f){
8431             
8432             if (!f.getName()) {
8433                 return;
8434             }
8435             
8436             var v = f.getValue();
8437             
8438             if (f.inputType =='radio') {
8439                 if (typeof(ret[f.getName()]) == 'undefined') {
8440                     ret[f.getName()] = ''; // empty..
8441                 }
8442
8443                 if (!f.el.dom.checked) {
8444                     return;
8445
8446                 }
8447                 v = f.el.dom.value;
8448
8449             }
8450             
8451             if(f.xtype == 'MoneyField'){
8452                 ret[f.currencyName] = f.getCurrency();
8453             }
8454
8455             // not sure if this supported any more..
8456             if ((typeof(v) == 'object') && f.getRawValue) {
8457                 v = f.getRawValue() ; // dates..
8458             }
8459             // combo boxes where name != hiddenName...
8460             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8461                 ret[f.name] = f.getRawValue();
8462             }
8463             ret[f.getName()] = v;
8464         });
8465
8466         return ret;
8467     },
8468
8469     /**
8470      * Clears all invalid messages in this form.
8471      * @return {BasicForm} this
8472      */
8473     clearInvalid : function(){
8474         var items = this.getItems();
8475
8476         items.each(function(f){
8477            f.clearInvalid();
8478         });
8479
8480         return this;
8481     },
8482
8483     /**
8484      * Resets this form.
8485      * @return {BasicForm} this
8486      */
8487     reset : function(){
8488         var items = this.getItems();
8489         items.each(function(f){
8490             f.reset();
8491         });
8492
8493         Roo.each(this.childForms || [], function (f) {
8494             f.reset();
8495         });
8496
8497
8498         return this;
8499     },
8500     
8501     getItems : function()
8502     {
8503         var r=new Roo.util.MixedCollection(false, function(o){
8504             return o.id || (o.id = Roo.id());
8505         });
8506         var iter = function(el) {
8507             if (el.inputEl) {
8508                 r.add(el);
8509             }
8510             if (!el.items) {
8511                 return;
8512             }
8513             Roo.each(el.items,function(e) {
8514                 iter(e);
8515             });
8516         };
8517
8518         iter(this);
8519         return r;
8520     },
8521     
8522     hideFields : function(items)
8523     {
8524         Roo.each(items, function(i){
8525             
8526             var f = this.findField(i);
8527             
8528             if(!f){
8529                 return;
8530             }
8531             
8532             f.hide();
8533             
8534         }, this);
8535     },
8536     
8537     showFields : function(items)
8538     {
8539         Roo.each(items, function(i){
8540             
8541             var f = this.findField(i);
8542             
8543             if(!f){
8544                 return;
8545             }
8546             
8547             f.show();
8548             
8549         }, this);
8550     }
8551
8552 });
8553
8554 Roo.apply(Roo.bootstrap.Form, {
8555     
8556     popover : {
8557         
8558         padding : 5,
8559         
8560         isApplied : false,
8561         
8562         isMasked : false,
8563         
8564         form : false,
8565         
8566         target : false,
8567         
8568         toolTip : false,
8569         
8570         intervalID : false,
8571         
8572         maskEl : false,
8573         
8574         apply : function()
8575         {
8576             if(this.isApplied){
8577                 return;
8578             }
8579             
8580             this.maskEl = {
8581                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8582                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8583                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8584                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8585             };
8586             
8587             this.maskEl.top.enableDisplayMode("block");
8588             this.maskEl.left.enableDisplayMode("block");
8589             this.maskEl.bottom.enableDisplayMode("block");
8590             this.maskEl.right.enableDisplayMode("block");
8591             
8592             this.toolTip = new Roo.bootstrap.Tooltip({
8593                 cls : 'roo-form-error-popover',
8594                 alignment : {
8595                     'left' : ['r-l', [-2,0], 'right'],
8596                     'right' : ['l-r', [2,0], 'left'],
8597                     'bottom' : ['tl-bl', [0,2], 'top'],
8598                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8599                 }
8600             });
8601             
8602             this.toolTip.render(Roo.get(document.body));
8603
8604             this.toolTip.el.enableDisplayMode("block");
8605             
8606             Roo.get(document.body).on('click', function(){
8607                 this.unmask();
8608             }, this);
8609             
8610             Roo.get(document.body).on('touchstart', function(){
8611                 this.unmask();
8612             }, this);
8613             
8614             this.isApplied = true
8615         },
8616         
8617         mask : function(form, target)
8618         {
8619             this.form = form;
8620             
8621             this.target = target;
8622             
8623             if(!this.form.errorMask || !target.el){
8624                 return;
8625             }
8626             
8627             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8628             
8629             Roo.log(scrollable);
8630             
8631             var ot = this.target.el.calcOffsetsTo(scrollable);
8632             
8633             var scrollTo = ot[1] - this.form.maskOffset;
8634             
8635             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8636             
8637             scrollable.scrollTo('top', scrollTo);
8638             
8639             var box = this.target.el.getBox();
8640             Roo.log(box);
8641             var zIndex = Roo.bootstrap.Modal.zIndex++;
8642
8643             
8644             this.maskEl.top.setStyle('position', 'absolute');
8645             this.maskEl.top.setStyle('z-index', zIndex);
8646             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8647             this.maskEl.top.setLeft(0);
8648             this.maskEl.top.setTop(0);
8649             this.maskEl.top.show();
8650             
8651             this.maskEl.left.setStyle('position', 'absolute');
8652             this.maskEl.left.setStyle('z-index', zIndex);
8653             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8654             this.maskEl.left.setLeft(0);
8655             this.maskEl.left.setTop(box.y - this.padding);
8656             this.maskEl.left.show();
8657
8658             this.maskEl.bottom.setStyle('position', 'absolute');
8659             this.maskEl.bottom.setStyle('z-index', zIndex);
8660             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8661             this.maskEl.bottom.setLeft(0);
8662             this.maskEl.bottom.setTop(box.bottom + this.padding);
8663             this.maskEl.bottom.show();
8664
8665             this.maskEl.right.setStyle('position', 'absolute');
8666             this.maskEl.right.setStyle('z-index', zIndex);
8667             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8668             this.maskEl.right.setLeft(box.right + this.padding);
8669             this.maskEl.right.setTop(box.y - this.padding);
8670             this.maskEl.right.show();
8671
8672             this.toolTip.bindEl = this.target.el;
8673
8674             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8675
8676             var tip = this.target.blankText;
8677
8678             if(this.target.getValue() !== '' ) {
8679                 
8680                 if (this.target.invalidText.length) {
8681                     tip = this.target.invalidText;
8682                 } else if (this.target.regexText.length){
8683                     tip = this.target.regexText;
8684                 }
8685             }
8686
8687             this.toolTip.show(tip);
8688
8689             this.intervalID = window.setInterval(function() {
8690                 Roo.bootstrap.Form.popover.unmask();
8691             }, 10000);
8692
8693             window.onwheel = function(){ return false;};
8694             
8695             (function(){ this.isMasked = true; }).defer(500, this);
8696             
8697         },
8698         
8699         unmask : function()
8700         {
8701             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8702                 return;
8703             }
8704             
8705             this.maskEl.top.setStyle('position', 'absolute');
8706             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8707             this.maskEl.top.hide();
8708
8709             this.maskEl.left.setStyle('position', 'absolute');
8710             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8711             this.maskEl.left.hide();
8712
8713             this.maskEl.bottom.setStyle('position', 'absolute');
8714             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8715             this.maskEl.bottom.hide();
8716
8717             this.maskEl.right.setStyle('position', 'absolute');
8718             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8719             this.maskEl.right.hide();
8720             
8721             this.toolTip.hide();
8722             
8723             this.toolTip.el.hide();
8724             
8725             window.onwheel = function(){ return true;};
8726             
8727             if(this.intervalID){
8728                 window.clearInterval(this.intervalID);
8729                 this.intervalID = false;
8730             }
8731             
8732             this.isMasked = false;
8733             
8734         }
8735         
8736     }
8737     
8738 });
8739
8740 /*
8741  * Based on:
8742  * Ext JS Library 1.1.1
8743  * Copyright(c) 2006-2007, Ext JS, LLC.
8744  *
8745  * Originally Released Under LGPL - original licence link has changed is not relivant.
8746  *
8747  * Fork - LGPL
8748  * <script type="text/javascript">
8749  */
8750 /**
8751  * @class Roo.form.VTypes
8752  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8753  * @singleton
8754  */
8755 Roo.form.VTypes = function(){
8756     // closure these in so they are only created once.
8757     var alpha = /^[a-zA-Z_]+$/;
8758     var alphanum = /^[a-zA-Z0-9_]+$/;
8759     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8760     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8761
8762     // All these messages and functions are configurable
8763     return {
8764         /**
8765          * The function used to validate email addresses
8766          * @param {String} value The email address
8767          */
8768         'email' : function(v){
8769             return email.test(v);
8770         },
8771         /**
8772          * The error text to display when the email validation function returns false
8773          * @type String
8774          */
8775         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8776         /**
8777          * The keystroke filter mask to be applied on email input
8778          * @type RegExp
8779          */
8780         'emailMask' : /[a-z0-9_\.\-@]/i,
8781
8782         /**
8783          * The function used to validate URLs
8784          * @param {String} value The URL
8785          */
8786         'url' : function(v){
8787             return url.test(v);
8788         },
8789         /**
8790          * The error text to display when the url validation function returns false
8791          * @type String
8792          */
8793         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8794         
8795         /**
8796          * The function used to validate alpha values
8797          * @param {String} value The value
8798          */
8799         'alpha' : function(v){
8800             return alpha.test(v);
8801         },
8802         /**
8803          * The error text to display when the alpha validation function returns false
8804          * @type String
8805          */
8806         'alphaText' : 'This field should only contain letters and _',
8807         /**
8808          * The keystroke filter mask to be applied on alpha input
8809          * @type RegExp
8810          */
8811         'alphaMask' : /[a-z_]/i,
8812
8813         /**
8814          * The function used to validate alphanumeric values
8815          * @param {String} value The value
8816          */
8817         'alphanum' : function(v){
8818             return alphanum.test(v);
8819         },
8820         /**
8821          * The error text to display when the alphanumeric validation function returns false
8822          * @type String
8823          */
8824         'alphanumText' : 'This field should only contain letters, numbers and _',
8825         /**
8826          * The keystroke filter mask to be applied on alphanumeric input
8827          * @type RegExp
8828          */
8829         'alphanumMask' : /[a-z0-9_]/i
8830     };
8831 }();/*
8832  * - LGPL
8833  *
8834  * Input
8835  * 
8836  */
8837
8838 /**
8839  * @class Roo.bootstrap.Input
8840  * @extends Roo.bootstrap.Component
8841  * Bootstrap Input class
8842  * @cfg {Boolean} disabled is it disabled
8843  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8844  * @cfg {String} name name of the input
8845  * @cfg {string} fieldLabel - the label associated
8846  * @cfg {string} placeholder - placeholder to put in text.
8847  * @cfg {string}  before - input group add on before
8848  * @cfg {string} after - input group add on after
8849  * @cfg {string} size - (lg|sm) or leave empty..
8850  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8851  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8852  * @cfg {Number} md colspan out of 12 for computer-sized screens
8853  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8854  * @cfg {string} value default value of the input
8855  * @cfg {Number} labelWidth set the width of label 
8856  * @cfg {Number} labellg set the width of label (1-12)
8857  * @cfg {Number} labelmd set the width of label (1-12)
8858  * @cfg {Number} labelsm set the width of label (1-12)
8859  * @cfg {Number} labelxs set the width of label (1-12)
8860  * @cfg {String} labelAlign (top|left)
8861  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8862  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8863  * @cfg {String} indicatorpos (left|right) default left
8864  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8865  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8866
8867  * @cfg {String} align (left|center|right) Default left
8868  * @cfg {Boolean} forceFeedback (true|false) Default false
8869  * 
8870  * @constructor
8871  * Create a new Input
8872  * @param {Object} config The config object
8873  */
8874
8875 Roo.bootstrap.Input = function(config){
8876     
8877     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8878     
8879     this.addEvents({
8880         /**
8881          * @event focus
8882          * Fires when this field receives input focus.
8883          * @param {Roo.form.Field} this
8884          */
8885         focus : true,
8886         /**
8887          * @event blur
8888          * Fires when this field loses input focus.
8889          * @param {Roo.form.Field} this
8890          */
8891         blur : true,
8892         /**
8893          * @event specialkey
8894          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8895          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8896          * @param {Roo.form.Field} this
8897          * @param {Roo.EventObject} e The event object
8898          */
8899         specialkey : true,
8900         /**
8901          * @event change
8902          * Fires just before the field blurs if the field value has changed.
8903          * @param {Roo.form.Field} this
8904          * @param {Mixed} newValue The new value
8905          * @param {Mixed} oldValue The original value
8906          */
8907         change : true,
8908         /**
8909          * @event invalid
8910          * Fires after the field has been marked as invalid.
8911          * @param {Roo.form.Field} this
8912          * @param {String} msg The validation message
8913          */
8914         invalid : true,
8915         /**
8916          * @event valid
8917          * Fires after the field has been validated with no errors.
8918          * @param {Roo.form.Field} this
8919          */
8920         valid : true,
8921          /**
8922          * @event keyup
8923          * Fires after the key up
8924          * @param {Roo.form.Field} this
8925          * @param {Roo.EventObject}  e The event Object
8926          */
8927         keyup : true
8928     });
8929 };
8930
8931 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8932      /**
8933      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8934       automatic validation (defaults to "keyup").
8935      */
8936     validationEvent : "keyup",
8937      /**
8938      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8939      */
8940     validateOnBlur : true,
8941     /**
8942      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8943      */
8944     validationDelay : 250,
8945      /**
8946      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8947      */
8948     focusClass : "x-form-focus",  // not needed???
8949     
8950        
8951     /**
8952      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8953      */
8954     invalidClass : "has-warning",
8955     
8956     /**
8957      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8958      */
8959     validClass : "has-success",
8960     
8961     /**
8962      * @cfg {Boolean} hasFeedback (true|false) default true
8963      */
8964     hasFeedback : true,
8965     
8966     /**
8967      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8968      */
8969     invalidFeedbackClass : "glyphicon-warning-sign",
8970     
8971     /**
8972      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8973      */
8974     validFeedbackClass : "glyphicon-ok",
8975     
8976     /**
8977      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8978      */
8979     selectOnFocus : false,
8980     
8981      /**
8982      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8983      */
8984     maskRe : null,
8985        /**
8986      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8987      */
8988     vtype : null,
8989     
8990       /**
8991      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8992      */
8993     disableKeyFilter : false,
8994     
8995        /**
8996      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8997      */
8998     disabled : false,
8999      /**
9000      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9001      */
9002     allowBlank : true,
9003     /**
9004      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9005      */
9006     blankText : "Please complete this mandatory field",
9007     
9008      /**
9009      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9010      */
9011     minLength : 0,
9012     /**
9013      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9014      */
9015     maxLength : Number.MAX_VALUE,
9016     /**
9017      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9018      */
9019     minLengthText : "The minimum length for this field is {0}",
9020     /**
9021      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9022      */
9023     maxLengthText : "The maximum length for this field is {0}",
9024   
9025     
9026     /**
9027      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9028      * If available, this function will be called only after the basic validators all return true, and will be passed the
9029      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9030      */
9031     validator : null,
9032     /**
9033      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9034      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9035      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9036      */
9037     regex : null,
9038     /**
9039      * @cfg {String} regexText -- Depricated - use Invalid Text
9040      */
9041     regexText : "",
9042     
9043     /**
9044      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9045      */
9046     invalidText : "",
9047     
9048     
9049     
9050     autocomplete: false,
9051     
9052     
9053     fieldLabel : '',
9054     inputType : 'text',
9055     
9056     name : false,
9057     placeholder: false,
9058     before : false,
9059     after : false,
9060     size : false,
9061     hasFocus : false,
9062     preventMark: false,
9063     isFormField : true,
9064     value : '',
9065     labelWidth : 2,
9066     labelAlign : false,
9067     readOnly : false,
9068     align : false,
9069     formatedValue : false,
9070     forceFeedback : false,
9071     
9072     indicatorpos : 'left',
9073     
9074     labellg : 0,
9075     labelmd : 0,
9076     labelsm : 0,
9077     labelxs : 0,
9078     
9079     capture : '',
9080     accept : '',
9081     
9082     parentLabelAlign : function()
9083     {
9084         var parent = this;
9085         while (parent.parent()) {
9086             parent = parent.parent();
9087             if (typeof(parent.labelAlign) !='undefined') {
9088                 return parent.labelAlign;
9089             }
9090         }
9091         return 'left';
9092         
9093     },
9094     
9095     getAutoCreate : function()
9096     {
9097         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9098         
9099         var id = Roo.id();
9100         
9101         var cfg = {};
9102         
9103         if(this.inputType != 'hidden'){
9104             cfg.cls = 'form-group' //input-group
9105         }
9106         
9107         var input =  {
9108             tag: 'input',
9109             id : id,
9110             type : this.inputType,
9111             value : this.value,
9112             cls : 'form-control',
9113             placeholder : this.placeholder || '',
9114             autocomplete : this.autocomplete || 'new-password'
9115         };
9116         
9117         if(this.capture.length){
9118             input.capture = this.capture;
9119         }
9120         
9121         if(this.accept.length){
9122             input.accept = this.accept + "/*";
9123         }
9124         
9125         if(this.align){
9126             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9127         }
9128         
9129         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9130             input.maxLength = this.maxLength;
9131         }
9132         
9133         if (this.disabled) {
9134             input.disabled=true;
9135         }
9136         
9137         if (this.readOnly) {
9138             input.readonly=true;
9139         }
9140         
9141         if (this.name) {
9142             input.name = this.name;
9143         }
9144         
9145         if (this.size) {
9146             input.cls += ' input-' + this.size;
9147         }
9148         
9149         var settings=this;
9150         ['xs','sm','md','lg'].map(function(size){
9151             if (settings[size]) {
9152                 cfg.cls += ' col-' + size + '-' + settings[size];
9153             }
9154         });
9155         
9156         var inputblock = input;
9157         
9158         var feedback = {
9159             tag: 'span',
9160             cls: 'glyphicon form-control-feedback'
9161         };
9162             
9163         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9164             
9165             inputblock = {
9166                 cls : 'has-feedback',
9167                 cn :  [
9168                     input,
9169                     feedback
9170                 ] 
9171             };  
9172         }
9173         
9174         if (this.before || this.after) {
9175             
9176             inputblock = {
9177                 cls : 'input-group',
9178                 cn :  [] 
9179             };
9180             
9181             if (this.before && typeof(this.before) == 'string') {
9182                 
9183                 inputblock.cn.push({
9184                     tag :'span',
9185                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9186                     html : this.before
9187                 });
9188             }
9189             if (this.before && typeof(this.before) == 'object') {
9190                 this.before = Roo.factory(this.before);
9191                 
9192                 inputblock.cn.push({
9193                     tag :'span',
9194                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9195                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9196                 });
9197             }
9198             
9199             inputblock.cn.push(input);
9200             
9201             if (this.after && typeof(this.after) == 'string') {
9202                 inputblock.cn.push({
9203                     tag :'span',
9204                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9205                     html : this.after
9206                 });
9207             }
9208             if (this.after && typeof(this.after) == 'object') {
9209                 this.after = Roo.factory(this.after);
9210                 
9211                 inputblock.cn.push({
9212                     tag :'span',
9213                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9214                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9215                 });
9216             }
9217             
9218             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9219                 inputblock.cls += ' has-feedback';
9220                 inputblock.cn.push(feedback);
9221             }
9222         };
9223         var indicator = {
9224             tag : 'i',
9225             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9226             tooltip : 'This field is required'
9227         };
9228         if (Roo.bootstrap.version == 4) {
9229             indicator = {
9230                 tag : 'i',
9231                 style : 'display-none'
9232             };
9233         }
9234         if (align ==='left' && this.fieldLabel.length) {
9235             
9236             cfg.cls += ' roo-form-group-label-left row';
9237             
9238             cfg.cn = [
9239                 indicator,
9240                 {
9241                     tag: 'label',
9242                     'for' :  id,
9243                     cls : 'control-label col-form-label',
9244                     html : this.fieldLabel
9245
9246                 },
9247                 {
9248                     cls : "", 
9249                     cn: [
9250                         inputblock
9251                     ]
9252                 }
9253             ];
9254             
9255             var labelCfg = cfg.cn[1];
9256             var contentCfg = cfg.cn[2];
9257             
9258             if(this.indicatorpos == 'right'){
9259                 cfg.cn = [
9260                     {
9261                         tag: 'label',
9262                         'for' :  id,
9263                         cls : 'control-label col-form-label',
9264                         cn : [
9265                             {
9266                                 tag : 'span',
9267                                 html : this.fieldLabel
9268                             },
9269                             indicator
9270                         ]
9271                     },
9272                     {
9273                         cls : "",
9274                         cn: [
9275                             inputblock
9276                         ]
9277                     }
9278
9279                 ];
9280                 
9281                 labelCfg = cfg.cn[0];
9282                 contentCfg = cfg.cn[1];
9283             
9284             }
9285             
9286             if(this.labelWidth > 12){
9287                 labelCfg.style = "width: " + this.labelWidth + 'px';
9288             }
9289             
9290             if(this.labelWidth < 13 && this.labelmd == 0){
9291                 this.labelmd = this.labelWidth;
9292             }
9293             
9294             if(this.labellg > 0){
9295                 labelCfg.cls += ' col-lg-' + this.labellg;
9296                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9297             }
9298             
9299             if(this.labelmd > 0){
9300                 labelCfg.cls += ' col-md-' + this.labelmd;
9301                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9302             }
9303             
9304             if(this.labelsm > 0){
9305                 labelCfg.cls += ' col-sm-' + this.labelsm;
9306                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9307             }
9308             
9309             if(this.labelxs > 0){
9310                 labelCfg.cls += ' col-xs-' + this.labelxs;
9311                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9312             }
9313             
9314             
9315         } else if ( this.fieldLabel.length) {
9316                 
9317             cfg.cn = [
9318                 {
9319                     tag : 'i',
9320                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9321                     tooltip : 'This field is required'
9322                 },
9323                 {
9324                     tag: 'label',
9325                    //cls : 'input-group-addon',
9326                     html : this.fieldLabel
9327
9328                 },
9329
9330                inputblock
9331
9332            ];
9333            
9334            if(this.indicatorpos == 'right'){
9335                 
9336                 cfg.cn = [
9337                     {
9338                         tag: 'label',
9339                        //cls : 'input-group-addon',
9340                         html : this.fieldLabel
9341
9342                     },
9343                     {
9344                         tag : 'i',
9345                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9346                         tooltip : 'This field is required'
9347                     },
9348
9349                    inputblock
9350
9351                ];
9352
9353             }
9354
9355         } else {
9356             
9357             cfg.cn = [
9358
9359                     inputblock
9360
9361             ];
9362                 
9363                 
9364         };
9365         
9366         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9367            cfg.cls += ' navbar-form';
9368         }
9369         
9370         if (this.parentType === 'NavGroup') {
9371            cfg.cls += ' navbar-form';
9372            cfg.tag = 'li';
9373         }
9374         
9375         return cfg;
9376         
9377     },
9378     /**
9379      * return the real input element.
9380      */
9381     inputEl: function ()
9382     {
9383         return this.el.select('input.form-control',true).first();
9384     },
9385     
9386     tooltipEl : function()
9387     {
9388         return this.inputEl();
9389     },
9390     
9391     indicatorEl : function()
9392     {
9393         if (Roo.bootstrap.version == 4) {
9394             return false; // not enabled in v4 yet.
9395         }
9396         
9397         var indicator = this.el.select('i.roo-required-indicator',true).first();
9398         
9399         if(!indicator){
9400             return false;
9401         }
9402         
9403         return indicator;
9404         
9405     },
9406     
9407     setDisabled : function(v)
9408     {
9409         var i  = this.inputEl().dom;
9410         if (!v) {
9411             i.removeAttribute('disabled');
9412             return;
9413             
9414         }
9415         i.setAttribute('disabled','true');
9416     },
9417     initEvents : function()
9418     {
9419           
9420         this.inputEl().on("keydown" , this.fireKey,  this);
9421         this.inputEl().on("focus", this.onFocus,  this);
9422         this.inputEl().on("blur", this.onBlur,  this);
9423         
9424         this.inputEl().relayEvent('keyup', this);
9425         
9426         this.indicator = this.indicatorEl();
9427         
9428         if(this.indicator){
9429             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9430         }
9431  
9432         // reference to original value for reset
9433         this.originalValue = this.getValue();
9434         //Roo.form.TextField.superclass.initEvents.call(this);
9435         if(this.validationEvent == 'keyup'){
9436             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9437             this.inputEl().on('keyup', this.filterValidation, this);
9438         }
9439         else if(this.validationEvent !== false){
9440             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9441         }
9442         
9443         if(this.selectOnFocus){
9444             this.on("focus", this.preFocus, this);
9445             
9446         }
9447         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9448             this.inputEl().on("keypress", this.filterKeys, this);
9449         } else {
9450             this.inputEl().relayEvent('keypress', this);
9451         }
9452        /* if(this.grow){
9453             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9454             this.el.on("click", this.autoSize,  this);
9455         }
9456         */
9457         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9458             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9459         }
9460         
9461         if (typeof(this.before) == 'object') {
9462             this.before.render(this.el.select('.roo-input-before',true).first());
9463         }
9464         if (typeof(this.after) == 'object') {
9465             this.after.render(this.el.select('.roo-input-after',true).first());
9466         }
9467         
9468         this.inputEl().on('change', this.onChange, this);
9469         
9470     },
9471     filterValidation : function(e){
9472         if(!e.isNavKeyPress()){
9473             this.validationTask.delay(this.validationDelay);
9474         }
9475     },
9476      /**
9477      * Validates the field value
9478      * @return {Boolean} True if the value is valid, else false
9479      */
9480     validate : function(){
9481         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9482         if(this.disabled || this.validateValue(this.getRawValue())){
9483             this.markValid();
9484             return true;
9485         }
9486         
9487         this.markInvalid();
9488         return false;
9489     },
9490     
9491     
9492     /**
9493      * Validates a value according to the field's validation rules and marks the field as invalid
9494      * if the validation fails
9495      * @param {Mixed} value The value to validate
9496      * @return {Boolean} True if the value is valid, else false
9497      */
9498     validateValue : function(value)
9499     {
9500         if(this.getVisibilityEl().hasClass('hidden')){
9501             return true;
9502         }
9503         
9504         if(value.length < 1)  { // if it's blank
9505             if(this.allowBlank){
9506                 return true;
9507             }
9508             return false;
9509         }
9510         
9511         if(value.length < this.minLength){
9512             return false;
9513         }
9514         if(value.length > this.maxLength){
9515             return false;
9516         }
9517         if(this.vtype){
9518             var vt = Roo.form.VTypes;
9519             if(!vt[this.vtype](value, this)){
9520                 return false;
9521             }
9522         }
9523         if(typeof this.validator == "function"){
9524             var msg = this.validator(value);
9525             if(msg !== true){
9526                 return false;
9527             }
9528             if (typeof(msg) == 'string') {
9529                 this.invalidText = msg;
9530             }
9531         }
9532         
9533         if(this.regex && !this.regex.test(value)){
9534             return false;
9535         }
9536         
9537         return true;
9538     },
9539     
9540      // private
9541     fireKey : function(e){
9542         //Roo.log('field ' + e.getKey());
9543         if(e.isNavKeyPress()){
9544             this.fireEvent("specialkey", this, e);
9545         }
9546     },
9547     focus : function (selectText){
9548         if(this.rendered){
9549             this.inputEl().focus();
9550             if(selectText === true){
9551                 this.inputEl().dom.select();
9552             }
9553         }
9554         return this;
9555     } ,
9556     
9557     onFocus : function(){
9558         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9559            // this.el.addClass(this.focusClass);
9560         }
9561         if(!this.hasFocus){
9562             this.hasFocus = true;
9563             this.startValue = this.getValue();
9564             this.fireEvent("focus", this);
9565         }
9566     },
9567     
9568     beforeBlur : Roo.emptyFn,
9569
9570     
9571     // private
9572     onBlur : function(){
9573         this.beforeBlur();
9574         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9575             //this.el.removeClass(this.focusClass);
9576         }
9577         this.hasFocus = false;
9578         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9579             this.validate();
9580         }
9581         var v = this.getValue();
9582         if(String(v) !== String(this.startValue)){
9583             this.fireEvent('change', this, v, this.startValue);
9584         }
9585         this.fireEvent("blur", this);
9586     },
9587     
9588     onChange : function(e)
9589     {
9590         var v = this.getValue();
9591         if(String(v) !== String(this.startValue)){
9592             this.fireEvent('change', this, v, this.startValue);
9593         }
9594         
9595     },
9596     
9597     /**
9598      * Resets the current field value to the originally loaded value and clears any validation messages
9599      */
9600     reset : function(){
9601         this.setValue(this.originalValue);
9602         this.validate();
9603     },
9604      /**
9605      * Returns the name of the field
9606      * @return {Mixed} name The name field
9607      */
9608     getName: function(){
9609         return this.name;
9610     },
9611      /**
9612      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9613      * @return {Mixed} value The field value
9614      */
9615     getValue : function(){
9616         
9617         var v = this.inputEl().getValue();
9618         
9619         return v;
9620     },
9621     /**
9622      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9623      * @return {Mixed} value The field value
9624      */
9625     getRawValue : function(){
9626         var v = this.inputEl().getValue();
9627         
9628         return v;
9629     },
9630     
9631     /**
9632      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9633      * @param {Mixed} value The value to set
9634      */
9635     setRawValue : function(v){
9636         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9637     },
9638     
9639     selectText : function(start, end){
9640         var v = this.getRawValue();
9641         if(v.length > 0){
9642             start = start === undefined ? 0 : start;
9643             end = end === undefined ? v.length : end;
9644             var d = this.inputEl().dom;
9645             if(d.setSelectionRange){
9646                 d.setSelectionRange(start, end);
9647             }else if(d.createTextRange){
9648                 var range = d.createTextRange();
9649                 range.moveStart("character", start);
9650                 range.moveEnd("character", v.length-end);
9651                 range.select();
9652             }
9653         }
9654     },
9655     
9656     /**
9657      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9658      * @param {Mixed} value The value to set
9659      */
9660     setValue : function(v){
9661         this.value = v;
9662         if(this.rendered){
9663             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9664             this.validate();
9665         }
9666     },
9667     
9668     /*
9669     processValue : function(value){
9670         if(this.stripCharsRe){
9671             var newValue = value.replace(this.stripCharsRe, '');
9672             if(newValue !== value){
9673                 this.setRawValue(newValue);
9674                 return newValue;
9675             }
9676         }
9677         return value;
9678     },
9679   */
9680     preFocus : function(){
9681         
9682         if(this.selectOnFocus){
9683             this.inputEl().dom.select();
9684         }
9685     },
9686     filterKeys : function(e){
9687         var k = e.getKey();
9688         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9689             return;
9690         }
9691         var c = e.getCharCode(), cc = String.fromCharCode(c);
9692         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9693             return;
9694         }
9695         if(!this.maskRe.test(cc)){
9696             e.stopEvent();
9697         }
9698     },
9699      /**
9700      * Clear any invalid styles/messages for this field
9701      */
9702     clearInvalid : function(){
9703         
9704         if(!this.el || this.preventMark){ // not rendered
9705             return;
9706         }
9707         
9708      
9709         this.el.removeClass(this.invalidClass);
9710         
9711         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9712             
9713             var feedback = this.el.select('.form-control-feedback', true).first();
9714             
9715             if(feedback){
9716                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9717             }
9718             
9719         }
9720         
9721         if(this.indicator){
9722             this.indicator.removeClass('visible');
9723             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9724         }
9725         
9726         this.fireEvent('valid', this);
9727     },
9728     
9729      /**
9730      * Mark this field as valid
9731      */
9732     markValid : function()
9733     {
9734         if(!this.el  || this.preventMark){ // not rendered...
9735             return;
9736         }
9737         
9738         this.el.removeClass([this.invalidClass, this.validClass]);
9739         
9740         var feedback = this.el.select('.form-control-feedback', true).first();
9741             
9742         if(feedback){
9743             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9744         }
9745         
9746         if(this.indicator){
9747             this.indicator.removeClass('visible');
9748             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9749         }
9750         
9751         if(this.disabled){
9752             return;
9753         }
9754         
9755         if(this.allowBlank && !this.getRawValue().length){
9756             return;
9757         }
9758         
9759         this.el.addClass(this.validClass);
9760         
9761         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9762             
9763             var feedback = this.el.select('.form-control-feedback', true).first();
9764             
9765             if(feedback){
9766                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9767                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9768             }
9769             
9770         }
9771         
9772         this.fireEvent('valid', this);
9773     },
9774     
9775      /**
9776      * Mark this field as invalid
9777      * @param {String} msg The validation message
9778      */
9779     markInvalid : function(msg)
9780     {
9781         if(!this.el  || this.preventMark){ // not rendered
9782             return;
9783         }
9784         
9785         this.el.removeClass([this.invalidClass, this.validClass]);
9786         
9787         var feedback = this.el.select('.form-control-feedback', true).first();
9788             
9789         if(feedback){
9790             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9791         }
9792
9793         if(this.disabled){
9794             return;
9795         }
9796         
9797         if(this.allowBlank && !this.getRawValue().length){
9798             return;
9799         }
9800         
9801         if(this.indicator){
9802             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9803             this.indicator.addClass('visible');
9804         }
9805         
9806         this.el.addClass(this.invalidClass);
9807         
9808         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9809             
9810             var feedback = this.el.select('.form-control-feedback', true).first();
9811             
9812             if(feedback){
9813                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9814                 
9815                 if(this.getValue().length || this.forceFeedback){
9816                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9817                 }
9818                 
9819             }
9820             
9821         }
9822         
9823         this.fireEvent('invalid', this, msg);
9824     },
9825     // private
9826     SafariOnKeyDown : function(event)
9827     {
9828         // this is a workaround for a password hang bug on chrome/ webkit.
9829         if (this.inputEl().dom.type != 'password') {
9830             return;
9831         }
9832         
9833         var isSelectAll = false;
9834         
9835         if(this.inputEl().dom.selectionEnd > 0){
9836             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9837         }
9838         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9839             event.preventDefault();
9840             this.setValue('');
9841             return;
9842         }
9843         
9844         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9845             
9846             event.preventDefault();
9847             // this is very hacky as keydown always get's upper case.
9848             //
9849             var cc = String.fromCharCode(event.getCharCode());
9850             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9851             
9852         }
9853     },
9854     adjustWidth : function(tag, w){
9855         tag = tag.toLowerCase();
9856         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9857             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9858                 if(tag == 'input'){
9859                     return w + 2;
9860                 }
9861                 if(tag == 'textarea'){
9862                     return w-2;
9863                 }
9864             }else if(Roo.isOpera){
9865                 if(tag == 'input'){
9866                     return w + 2;
9867                 }
9868                 if(tag == 'textarea'){
9869                     return w-2;
9870                 }
9871             }
9872         }
9873         return w;
9874     },
9875     
9876     setFieldLabel : function(v)
9877     {
9878         if(!this.rendered){
9879             return;
9880         }
9881         
9882         if(this.indicatorEl()){
9883             var ar = this.el.select('label > span',true);
9884             
9885             if (ar.elements.length) {
9886                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9887                 this.fieldLabel = v;
9888                 return;
9889             }
9890             
9891             var br = this.el.select('label',true);
9892             
9893             if(br.elements.length) {
9894                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9895                 this.fieldLabel = v;
9896                 return;
9897             }
9898             
9899             Roo.log('Cannot Found any of label > span || label in input');
9900             return;
9901         }
9902         
9903         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9904         this.fieldLabel = v;
9905         
9906         
9907     }
9908 });
9909
9910  
9911 /*
9912  * - LGPL
9913  *
9914  * Input
9915  * 
9916  */
9917
9918 /**
9919  * @class Roo.bootstrap.TextArea
9920  * @extends Roo.bootstrap.Input
9921  * Bootstrap TextArea class
9922  * @cfg {Number} cols Specifies the visible width of a text area
9923  * @cfg {Number} rows Specifies the visible number of lines in a text area
9924  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9925  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9926  * @cfg {string} html text
9927  * 
9928  * @constructor
9929  * Create a new TextArea
9930  * @param {Object} config The config object
9931  */
9932
9933 Roo.bootstrap.TextArea = function(config){
9934     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9935    
9936 };
9937
9938 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9939      
9940     cols : false,
9941     rows : 5,
9942     readOnly : false,
9943     warp : 'soft',
9944     resize : false,
9945     value: false,
9946     html: false,
9947     
9948     getAutoCreate : function(){
9949         
9950         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9951         
9952         var id = Roo.id();
9953         
9954         var cfg = {};
9955         
9956         if(this.inputType != 'hidden'){
9957             cfg.cls = 'form-group' //input-group
9958         }
9959         
9960         var input =  {
9961             tag: 'textarea',
9962             id : id,
9963             warp : this.warp,
9964             rows : this.rows,
9965             value : this.value || '',
9966             html: this.html || '',
9967             cls : 'form-control',
9968             placeholder : this.placeholder || '' 
9969             
9970         };
9971         
9972         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9973             input.maxLength = this.maxLength;
9974         }
9975         
9976         if(this.resize){
9977             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9978         }
9979         
9980         if(this.cols){
9981             input.cols = this.cols;
9982         }
9983         
9984         if (this.readOnly) {
9985             input.readonly = true;
9986         }
9987         
9988         if (this.name) {
9989             input.name = this.name;
9990         }
9991         
9992         if (this.size) {
9993             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9994         }
9995         
9996         var settings=this;
9997         ['xs','sm','md','lg'].map(function(size){
9998             if (settings[size]) {
9999                 cfg.cls += ' col-' + size + '-' + settings[size];
10000             }
10001         });
10002         
10003         var inputblock = input;
10004         
10005         if(this.hasFeedback && !this.allowBlank){
10006             
10007             var feedback = {
10008                 tag: 'span',
10009                 cls: 'glyphicon form-control-feedback'
10010             };
10011
10012             inputblock = {
10013                 cls : 'has-feedback',
10014                 cn :  [
10015                     input,
10016                     feedback
10017                 ] 
10018             };  
10019         }
10020         
10021         
10022         if (this.before || this.after) {
10023             
10024             inputblock = {
10025                 cls : 'input-group',
10026                 cn :  [] 
10027             };
10028             if (this.before) {
10029                 inputblock.cn.push({
10030                     tag :'span',
10031                     cls : 'input-group-addon',
10032                     html : this.before
10033                 });
10034             }
10035             
10036             inputblock.cn.push(input);
10037             
10038             if(this.hasFeedback && !this.allowBlank){
10039                 inputblock.cls += ' has-feedback';
10040                 inputblock.cn.push(feedback);
10041             }
10042             
10043             if (this.after) {
10044                 inputblock.cn.push({
10045                     tag :'span',
10046                     cls : 'input-group-addon',
10047                     html : this.after
10048                 });
10049             }
10050             
10051         }
10052         
10053         if (align ==='left' && this.fieldLabel.length) {
10054             cfg.cn = [
10055                 {
10056                     tag: 'label',
10057                     'for' :  id,
10058                     cls : 'control-label',
10059                     html : this.fieldLabel
10060                 },
10061                 {
10062                     cls : "",
10063                     cn: [
10064                         inputblock
10065                     ]
10066                 }
10067
10068             ];
10069             
10070             if(this.labelWidth > 12){
10071                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10072             }
10073
10074             if(this.labelWidth < 13 && this.labelmd == 0){
10075                 this.labelmd = this.labelWidth;
10076             }
10077
10078             if(this.labellg > 0){
10079                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10080                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10081             }
10082
10083             if(this.labelmd > 0){
10084                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10085                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10086             }
10087
10088             if(this.labelsm > 0){
10089                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10090                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10091             }
10092
10093             if(this.labelxs > 0){
10094                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10095                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10096             }
10097             
10098         } else if ( this.fieldLabel.length) {
10099             cfg.cn = [
10100
10101                {
10102                    tag: 'label',
10103                    //cls : 'input-group-addon',
10104                    html : this.fieldLabel
10105
10106                },
10107
10108                inputblock
10109
10110            ];
10111
10112         } else {
10113
10114             cfg.cn = [
10115
10116                 inputblock
10117
10118             ];
10119                 
10120         }
10121         
10122         if (this.disabled) {
10123             input.disabled=true;
10124         }
10125         
10126         return cfg;
10127         
10128     },
10129     /**
10130      * return the real textarea element.
10131      */
10132     inputEl: function ()
10133     {
10134         return this.el.select('textarea.form-control',true).first();
10135     },
10136     
10137     /**
10138      * Clear any invalid styles/messages for this field
10139      */
10140     clearInvalid : function()
10141     {
10142         
10143         if(!this.el || this.preventMark){ // not rendered
10144             return;
10145         }
10146         
10147         var label = this.el.select('label', true).first();
10148         var icon = this.el.select('i.fa-star', true).first();
10149         
10150         if(label && icon){
10151             icon.remove();
10152         }
10153         
10154         this.el.removeClass(this.invalidClass);
10155         
10156         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10157             
10158             var feedback = this.el.select('.form-control-feedback', true).first();
10159             
10160             if(feedback){
10161                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10162             }
10163             
10164         }
10165         
10166         this.fireEvent('valid', this);
10167     },
10168     
10169      /**
10170      * Mark this field as valid
10171      */
10172     markValid : function()
10173     {
10174         if(!this.el  || this.preventMark){ // not rendered
10175             return;
10176         }
10177         
10178         this.el.removeClass([this.invalidClass, this.validClass]);
10179         
10180         var feedback = this.el.select('.form-control-feedback', true).first();
10181             
10182         if(feedback){
10183             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10184         }
10185
10186         if(this.disabled || this.allowBlank){
10187             return;
10188         }
10189         
10190         var label = this.el.select('label', true).first();
10191         var icon = this.el.select('i.fa-star', true).first();
10192         
10193         if(label && icon){
10194             icon.remove();
10195         }
10196         
10197         this.el.addClass(this.validClass);
10198         
10199         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10200             
10201             var feedback = this.el.select('.form-control-feedback', true).first();
10202             
10203             if(feedback){
10204                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10205                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10206             }
10207             
10208         }
10209         
10210         this.fireEvent('valid', this);
10211     },
10212     
10213      /**
10214      * Mark this field as invalid
10215      * @param {String} msg The validation message
10216      */
10217     markInvalid : function(msg)
10218     {
10219         if(!this.el  || this.preventMark){ // not rendered
10220             return;
10221         }
10222         
10223         this.el.removeClass([this.invalidClass, this.validClass]);
10224         
10225         var feedback = this.el.select('.form-control-feedback', true).first();
10226             
10227         if(feedback){
10228             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10229         }
10230
10231         if(this.disabled || this.allowBlank){
10232             return;
10233         }
10234         
10235         var label = this.el.select('label', true).first();
10236         var icon = this.el.select('i.fa-star', true).first();
10237         
10238         if(!this.getValue().length && label && !icon){
10239             this.el.createChild({
10240                 tag : 'i',
10241                 cls : 'text-danger fa fa-lg fa-star',
10242                 tooltip : 'This field is required',
10243                 style : 'margin-right:5px;'
10244             }, label, true);
10245         }
10246
10247         this.el.addClass(this.invalidClass);
10248         
10249         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10250             
10251             var feedback = this.el.select('.form-control-feedback', true).first();
10252             
10253             if(feedback){
10254                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10255                 
10256                 if(this.getValue().length || this.forceFeedback){
10257                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10258                 }
10259                 
10260             }
10261             
10262         }
10263         
10264         this.fireEvent('invalid', this, msg);
10265     }
10266 });
10267
10268  
10269 /*
10270  * - LGPL
10271  *
10272  * trigger field - base class for combo..
10273  * 
10274  */
10275  
10276 /**
10277  * @class Roo.bootstrap.TriggerField
10278  * @extends Roo.bootstrap.Input
10279  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10280  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10281  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10282  * for which you can provide a custom implementation.  For example:
10283  * <pre><code>
10284 var trigger = new Roo.bootstrap.TriggerField();
10285 trigger.onTriggerClick = myTriggerFn;
10286 trigger.applyTo('my-field');
10287 </code></pre>
10288  *
10289  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10290  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10291  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10292  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10293  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10294
10295  * @constructor
10296  * Create a new TriggerField.
10297  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10298  * to the base TextField)
10299  */
10300 Roo.bootstrap.TriggerField = function(config){
10301     this.mimicing = false;
10302     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10303 };
10304
10305 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10306     /**
10307      * @cfg {String} triggerClass A CSS class to apply to the trigger
10308      */
10309      /**
10310      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10311      */
10312     hideTrigger:false,
10313
10314     /**
10315      * @cfg {Boolean} removable (true|false) special filter default false
10316      */
10317     removable : false,
10318     
10319     /** @cfg {Boolean} grow @hide */
10320     /** @cfg {Number} growMin @hide */
10321     /** @cfg {Number} growMax @hide */
10322
10323     /**
10324      * @hide 
10325      * @method
10326      */
10327     autoSize: Roo.emptyFn,
10328     // private
10329     monitorTab : true,
10330     // private
10331     deferHeight : true,
10332
10333     
10334     actionMode : 'wrap',
10335     
10336     caret : false,
10337     
10338     
10339     getAutoCreate : function(){
10340        
10341         var align = this.labelAlign || this.parentLabelAlign();
10342         
10343         var id = Roo.id();
10344         
10345         var cfg = {
10346             cls: 'form-group' //input-group
10347         };
10348         
10349         
10350         var input =  {
10351             tag: 'input',
10352             id : id,
10353             type : this.inputType,
10354             cls : 'form-control',
10355             autocomplete: 'new-password',
10356             placeholder : this.placeholder || '' 
10357             
10358         };
10359         if (this.name) {
10360             input.name = this.name;
10361         }
10362         if (this.size) {
10363             input.cls += ' input-' + this.size;
10364         }
10365         
10366         if (this.disabled) {
10367             input.disabled=true;
10368         }
10369         
10370         var inputblock = input;
10371         
10372         if(this.hasFeedback && !this.allowBlank){
10373             
10374             var feedback = {
10375                 tag: 'span',
10376                 cls: 'glyphicon form-control-feedback'
10377             };
10378             
10379             if(this.removable && !this.editable && !this.tickable){
10380                 inputblock = {
10381                     cls : 'has-feedback',
10382                     cn :  [
10383                         inputblock,
10384                         {
10385                             tag: 'button',
10386                             html : 'x',
10387                             cls : 'roo-combo-removable-btn close'
10388                         },
10389                         feedback
10390                     ] 
10391                 };
10392             } else {
10393                 inputblock = {
10394                     cls : 'has-feedback',
10395                     cn :  [
10396                         inputblock,
10397                         feedback
10398                     ] 
10399                 };
10400             }
10401
10402         } else {
10403             if(this.removable && !this.editable && !this.tickable){
10404                 inputblock = {
10405                     cls : 'roo-removable',
10406                     cn :  [
10407                         inputblock,
10408                         {
10409                             tag: 'button',
10410                             html : 'x',
10411                             cls : 'roo-combo-removable-btn close'
10412                         }
10413                     ] 
10414                 };
10415             }
10416         }
10417         
10418         if (this.before || this.after) {
10419             
10420             inputblock = {
10421                 cls : 'input-group',
10422                 cn :  [] 
10423             };
10424             if (this.before) {
10425                 inputblock.cn.push({
10426                     tag :'span',
10427                     cls : 'input-group-addon input-group-prepend input-group-text',
10428                     html : this.before
10429                 });
10430             }
10431             
10432             inputblock.cn.push(input);
10433             
10434             if(this.hasFeedback && !this.allowBlank){
10435                 inputblock.cls += ' has-feedback';
10436                 inputblock.cn.push(feedback);
10437             }
10438             
10439             if (this.after) {
10440                 inputblock.cn.push({
10441                     tag :'span',
10442                     cls : 'input-group-addon input-group-append input-group-text',
10443                     html : this.after
10444                 });
10445             }
10446             
10447         };
10448         
10449       
10450         
10451         var ibwrap = inputblock;
10452         
10453         if(this.multiple){
10454             ibwrap = {
10455                 tag: 'ul',
10456                 cls: 'roo-select2-choices',
10457                 cn:[
10458                     {
10459                         tag: 'li',
10460                         cls: 'roo-select2-search-field',
10461                         cn: [
10462
10463                             inputblock
10464                         ]
10465                     }
10466                 ]
10467             };
10468                 
10469         }
10470         
10471         var combobox = {
10472             cls: 'roo-select2-container input-group',
10473             cn: [
10474                  {
10475                     tag: 'input',
10476                     type : 'hidden',
10477                     cls: 'form-hidden-field'
10478                 },
10479                 ibwrap
10480             ]
10481         };
10482         
10483         if(!this.multiple && this.showToggleBtn){
10484             
10485             var caret = {
10486                         tag: 'span',
10487                         cls: 'caret'
10488              };
10489             if (this.caret != false) {
10490                 caret = {
10491                      tag: 'i',
10492                      cls: 'fa fa-' + this.caret
10493                 };
10494                 
10495             }
10496             
10497             combobox.cn.push({
10498                 tag :'span',
10499                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10500                 cn : [
10501                     caret,
10502                     {
10503                         tag: 'span',
10504                         cls: 'combobox-clear',
10505                         cn  : [
10506                             {
10507                                 tag : 'i',
10508                                 cls: 'icon-remove'
10509                             }
10510                         ]
10511                     }
10512                 ]
10513
10514             })
10515         }
10516         
10517         if(this.multiple){
10518             combobox.cls += ' roo-select2-container-multi';
10519         }
10520          var indicator = {
10521             tag : 'i',
10522             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10523             tooltip : 'This field is required'
10524         };
10525         if (Roo.bootstrap.version == 4) {
10526             indicator = {
10527                 tag : 'i',
10528                 style : 'display:none'
10529             };
10530         }
10531         
10532         
10533         if (align ==='left' && this.fieldLabel.length) {
10534             
10535             cfg.cls += ' roo-form-group-label-left row';
10536
10537             cfg.cn = [
10538                 indicator,
10539                 {
10540                     tag: 'label',
10541                     'for' :  id,
10542                     cls : 'control-label',
10543                     html : this.fieldLabel
10544
10545                 },
10546                 {
10547                     cls : "", 
10548                     cn: [
10549                         combobox
10550                     ]
10551                 }
10552
10553             ];
10554             
10555             var labelCfg = cfg.cn[1];
10556             var contentCfg = cfg.cn[2];
10557             
10558             if(this.indicatorpos == 'right'){
10559                 cfg.cn = [
10560                     {
10561                         tag: 'label',
10562                         'for' :  id,
10563                         cls : 'control-label',
10564                         cn : [
10565                             {
10566                                 tag : 'span',
10567                                 html : this.fieldLabel
10568                             },
10569                             indicator
10570                         ]
10571                     },
10572                     {
10573                         cls : "", 
10574                         cn: [
10575                             combobox
10576                         ]
10577                     }
10578
10579                 ];
10580                 
10581                 labelCfg = cfg.cn[0];
10582                 contentCfg = cfg.cn[1];
10583             }
10584             
10585             if(this.labelWidth > 12){
10586                 labelCfg.style = "width: " + this.labelWidth + 'px';
10587             }
10588             
10589             if(this.labelWidth < 13 && this.labelmd == 0){
10590                 this.labelmd = this.labelWidth;
10591             }
10592             
10593             if(this.labellg > 0){
10594                 labelCfg.cls += ' col-lg-' + this.labellg;
10595                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10596             }
10597             
10598             if(this.labelmd > 0){
10599                 labelCfg.cls += ' col-md-' + this.labelmd;
10600                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10601             }
10602             
10603             if(this.labelsm > 0){
10604                 labelCfg.cls += ' col-sm-' + this.labelsm;
10605                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10606             }
10607             
10608             if(this.labelxs > 0){
10609                 labelCfg.cls += ' col-xs-' + this.labelxs;
10610                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10611             }
10612             
10613         } else if ( this.fieldLabel.length) {
10614 //                Roo.log(" label");
10615             cfg.cn = [
10616                 indicator,
10617                {
10618                    tag: 'label',
10619                    //cls : 'input-group-addon',
10620                    html : this.fieldLabel
10621
10622                },
10623
10624                combobox
10625
10626             ];
10627             
10628             if(this.indicatorpos == 'right'){
10629                 
10630                 cfg.cn = [
10631                     {
10632                        tag: 'label',
10633                        cn : [
10634                            {
10635                                tag : 'span',
10636                                html : this.fieldLabel
10637                            },
10638                            indicator
10639                        ]
10640
10641                     },
10642                     combobox
10643
10644                 ];
10645
10646             }
10647
10648         } else {
10649             
10650 //                Roo.log(" no label && no align");
10651                 cfg = combobox
10652                      
10653                 
10654         }
10655         
10656         var settings=this;
10657         ['xs','sm','md','lg'].map(function(size){
10658             if (settings[size]) {
10659                 cfg.cls += ' col-' + size + '-' + settings[size];
10660             }
10661         });
10662         
10663         return cfg;
10664         
10665     },
10666     
10667     
10668     
10669     // private
10670     onResize : function(w, h){
10671 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10672 //        if(typeof w == 'number'){
10673 //            var x = w - this.trigger.getWidth();
10674 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10675 //            this.trigger.setStyle('left', x+'px');
10676 //        }
10677     },
10678
10679     // private
10680     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10681
10682     // private
10683     getResizeEl : function(){
10684         return this.inputEl();
10685     },
10686
10687     // private
10688     getPositionEl : function(){
10689         return this.inputEl();
10690     },
10691
10692     // private
10693     alignErrorIcon : function(){
10694         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10695     },
10696
10697     // private
10698     initEvents : function(){
10699         
10700         this.createList();
10701         
10702         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10703         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10704         if(!this.multiple && this.showToggleBtn){
10705             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10706             if(this.hideTrigger){
10707                 this.trigger.setDisplayed(false);
10708             }
10709             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10710         }
10711         
10712         if(this.multiple){
10713             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10714         }
10715         
10716         if(this.removable && !this.editable && !this.tickable){
10717             var close = this.closeTriggerEl();
10718             
10719             if(close){
10720                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10721                 close.on('click', this.removeBtnClick, this, close);
10722             }
10723         }
10724         
10725         //this.trigger.addClassOnOver('x-form-trigger-over');
10726         //this.trigger.addClassOnClick('x-form-trigger-click');
10727         
10728         //if(!this.width){
10729         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10730         //}
10731     },
10732     
10733     closeTriggerEl : function()
10734     {
10735         var close = this.el.select('.roo-combo-removable-btn', true).first();
10736         return close ? close : false;
10737     },
10738     
10739     removeBtnClick : function(e, h, el)
10740     {
10741         e.preventDefault();
10742         
10743         if(this.fireEvent("remove", this) !== false){
10744             this.reset();
10745             this.fireEvent("afterremove", this)
10746         }
10747     },
10748     
10749     createList : function()
10750     {
10751         this.list = Roo.get(document.body).createChild({
10752             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10753             cls: 'typeahead typeahead-long dropdown-menu',
10754             style: 'display:none'
10755         });
10756         
10757         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10758         
10759     },
10760
10761     // private
10762     initTrigger : function(){
10763        
10764     },
10765
10766     // private
10767     onDestroy : function(){
10768         if(this.trigger){
10769             this.trigger.removeAllListeners();
10770           //  this.trigger.remove();
10771         }
10772         //if(this.wrap){
10773         //    this.wrap.remove();
10774         //}
10775         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10776     },
10777
10778     // private
10779     onFocus : function(){
10780         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10781         /*
10782         if(!this.mimicing){
10783             this.wrap.addClass('x-trigger-wrap-focus');
10784             this.mimicing = true;
10785             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10786             if(this.monitorTab){
10787                 this.el.on("keydown", this.checkTab, this);
10788             }
10789         }
10790         */
10791     },
10792
10793     // private
10794     checkTab : function(e){
10795         if(e.getKey() == e.TAB){
10796             this.triggerBlur();
10797         }
10798     },
10799
10800     // private
10801     onBlur : function(){
10802         // do nothing
10803     },
10804
10805     // private
10806     mimicBlur : function(e, t){
10807         /*
10808         if(!this.wrap.contains(t) && this.validateBlur()){
10809             this.triggerBlur();
10810         }
10811         */
10812     },
10813
10814     // private
10815     triggerBlur : function(){
10816         this.mimicing = false;
10817         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10818         if(this.monitorTab){
10819             this.el.un("keydown", this.checkTab, this);
10820         }
10821         //this.wrap.removeClass('x-trigger-wrap-focus');
10822         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10823     },
10824
10825     // private
10826     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10827     validateBlur : function(e, t){
10828         return true;
10829     },
10830
10831     // private
10832     onDisable : function(){
10833         this.inputEl().dom.disabled = true;
10834         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10835         //if(this.wrap){
10836         //    this.wrap.addClass('x-item-disabled');
10837         //}
10838     },
10839
10840     // private
10841     onEnable : function(){
10842         this.inputEl().dom.disabled = false;
10843         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10844         //if(this.wrap){
10845         //    this.el.removeClass('x-item-disabled');
10846         //}
10847     },
10848
10849     // private
10850     onShow : function(){
10851         var ae = this.getActionEl();
10852         
10853         if(ae){
10854             ae.dom.style.display = '';
10855             ae.dom.style.visibility = 'visible';
10856         }
10857     },
10858
10859     // private
10860     
10861     onHide : function(){
10862         var ae = this.getActionEl();
10863         ae.dom.style.display = 'none';
10864     },
10865
10866     /**
10867      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10868      * by an implementing function.
10869      * @method
10870      * @param {EventObject} e
10871      */
10872     onTriggerClick : Roo.emptyFn
10873 });
10874  /*
10875  * Based on:
10876  * Ext JS Library 1.1.1
10877  * Copyright(c) 2006-2007, Ext JS, LLC.
10878  *
10879  * Originally Released Under LGPL - original licence link has changed is not relivant.
10880  *
10881  * Fork - LGPL
10882  * <script type="text/javascript">
10883  */
10884
10885
10886 /**
10887  * @class Roo.data.SortTypes
10888  * @singleton
10889  * Defines the default sorting (casting?) comparison functions used when sorting data.
10890  */
10891 Roo.data.SortTypes = {
10892     /**
10893      * Default sort that does nothing
10894      * @param {Mixed} s The value being converted
10895      * @return {Mixed} The comparison value
10896      */
10897     none : function(s){
10898         return s;
10899     },
10900     
10901     /**
10902      * The regular expression used to strip tags
10903      * @type {RegExp}
10904      * @property
10905      */
10906     stripTagsRE : /<\/?[^>]+>/gi,
10907     
10908     /**
10909      * Strips all HTML tags to sort on text only
10910      * @param {Mixed} s The value being converted
10911      * @return {String} The comparison value
10912      */
10913     asText : function(s){
10914         return String(s).replace(this.stripTagsRE, "");
10915     },
10916     
10917     /**
10918      * Strips all HTML tags to sort on text only - Case insensitive
10919      * @param {Mixed} s The value being converted
10920      * @return {String} The comparison value
10921      */
10922     asUCText : function(s){
10923         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10924     },
10925     
10926     /**
10927      * Case insensitive string
10928      * @param {Mixed} s The value being converted
10929      * @return {String} The comparison value
10930      */
10931     asUCString : function(s) {
10932         return String(s).toUpperCase();
10933     },
10934     
10935     /**
10936      * Date sorting
10937      * @param {Mixed} s The value being converted
10938      * @return {Number} The comparison value
10939      */
10940     asDate : function(s) {
10941         if(!s){
10942             return 0;
10943         }
10944         if(s instanceof Date){
10945             return s.getTime();
10946         }
10947         return Date.parse(String(s));
10948     },
10949     
10950     /**
10951      * Float sorting
10952      * @param {Mixed} s The value being converted
10953      * @return {Float} The comparison value
10954      */
10955     asFloat : function(s) {
10956         var val = parseFloat(String(s).replace(/,/g, ""));
10957         if(isNaN(val)) {
10958             val = 0;
10959         }
10960         return val;
10961     },
10962     
10963     /**
10964      * Integer sorting
10965      * @param {Mixed} s The value being converted
10966      * @return {Number} The comparison value
10967      */
10968     asInt : function(s) {
10969         var val = parseInt(String(s).replace(/,/g, ""));
10970         if(isNaN(val)) {
10971             val = 0;
10972         }
10973         return val;
10974     }
10975 };/*
10976  * Based on:
10977  * Ext JS Library 1.1.1
10978  * Copyright(c) 2006-2007, Ext JS, LLC.
10979  *
10980  * Originally Released Under LGPL - original licence link has changed is not relivant.
10981  *
10982  * Fork - LGPL
10983  * <script type="text/javascript">
10984  */
10985
10986 /**
10987 * @class Roo.data.Record
10988  * Instances of this class encapsulate both record <em>definition</em> information, and record
10989  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10990  * to access Records cached in an {@link Roo.data.Store} object.<br>
10991  * <p>
10992  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10993  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10994  * objects.<br>
10995  * <p>
10996  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10997  * @constructor
10998  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10999  * {@link #create}. The parameters are the same.
11000  * @param {Array} data An associative Array of data values keyed by the field name.
11001  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11002  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11003  * not specified an integer id is generated.
11004  */
11005 Roo.data.Record = function(data, id){
11006     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11007     this.data = data;
11008 };
11009
11010 /**
11011  * Generate a constructor for a specific record layout.
11012  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11013  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11014  * Each field definition object may contain the following properties: <ul>
11015  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
11016  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11017  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11018  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11019  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11020  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11021  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11022  * this may be omitted.</p></li>
11023  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11024  * <ul><li>auto (Default, implies no conversion)</li>
11025  * <li>string</li>
11026  * <li>int</li>
11027  * <li>float</li>
11028  * <li>boolean</li>
11029  * <li>date</li></ul></p></li>
11030  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11031  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11032  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11033  * by the Reader into an object that will be stored in the Record. It is passed the
11034  * following parameters:<ul>
11035  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11036  * </ul></p></li>
11037  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11038  * </ul>
11039  * <br>usage:<br><pre><code>
11040 var TopicRecord = Roo.data.Record.create(
11041     {name: 'title', mapping: 'topic_title'},
11042     {name: 'author', mapping: 'username'},
11043     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11044     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11045     {name: 'lastPoster', mapping: 'user2'},
11046     {name: 'excerpt', mapping: 'post_text'}
11047 );
11048
11049 var myNewRecord = new TopicRecord({
11050     title: 'Do my job please',
11051     author: 'noobie',
11052     totalPosts: 1,
11053     lastPost: new Date(),
11054     lastPoster: 'Animal',
11055     excerpt: 'No way dude!'
11056 });
11057 myStore.add(myNewRecord);
11058 </code></pre>
11059  * @method create
11060  * @static
11061  */
11062 Roo.data.Record.create = function(o){
11063     var f = function(){
11064         f.superclass.constructor.apply(this, arguments);
11065     };
11066     Roo.extend(f, Roo.data.Record);
11067     var p = f.prototype;
11068     p.fields = new Roo.util.MixedCollection(false, function(field){
11069         return field.name;
11070     });
11071     for(var i = 0, len = o.length; i < len; i++){
11072         p.fields.add(new Roo.data.Field(o[i]));
11073     }
11074     f.getField = function(name){
11075         return p.fields.get(name);  
11076     };
11077     return f;
11078 };
11079
11080 Roo.data.Record.AUTO_ID = 1000;
11081 Roo.data.Record.EDIT = 'edit';
11082 Roo.data.Record.REJECT = 'reject';
11083 Roo.data.Record.COMMIT = 'commit';
11084
11085 Roo.data.Record.prototype = {
11086     /**
11087      * Readonly flag - true if this record has been modified.
11088      * @type Boolean
11089      */
11090     dirty : false,
11091     editing : false,
11092     error: null,
11093     modified: null,
11094
11095     // private
11096     join : function(store){
11097         this.store = store;
11098     },
11099
11100     /**
11101      * Set the named field to the specified value.
11102      * @param {String} name The name of the field to set.
11103      * @param {Object} value The value to set the field to.
11104      */
11105     set : function(name, value){
11106         if(this.data[name] == value){
11107             return;
11108         }
11109         this.dirty = true;
11110         if(!this.modified){
11111             this.modified = {};
11112         }
11113         if(typeof this.modified[name] == 'undefined'){
11114             this.modified[name] = this.data[name];
11115         }
11116         this.data[name] = value;
11117         if(!this.editing && this.store){
11118             this.store.afterEdit(this);
11119         }       
11120     },
11121
11122     /**
11123      * Get the value of the named field.
11124      * @param {String} name The name of the field to get the value of.
11125      * @return {Object} The value of the field.
11126      */
11127     get : function(name){
11128         return this.data[name]; 
11129     },
11130
11131     // private
11132     beginEdit : function(){
11133         this.editing = true;
11134         this.modified = {}; 
11135     },
11136
11137     // private
11138     cancelEdit : function(){
11139         this.editing = false;
11140         delete this.modified;
11141     },
11142
11143     // private
11144     endEdit : function(){
11145         this.editing = false;
11146         if(this.dirty && this.store){
11147             this.store.afterEdit(this);
11148         }
11149     },
11150
11151     /**
11152      * Usually called by the {@link Roo.data.Store} which owns the Record.
11153      * Rejects all changes made to the Record since either creation, or the last commit operation.
11154      * Modified fields are reverted to their original values.
11155      * <p>
11156      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11157      * of reject operations.
11158      */
11159     reject : function(){
11160         var m = this.modified;
11161         for(var n in m){
11162             if(typeof m[n] != "function"){
11163                 this.data[n] = m[n];
11164             }
11165         }
11166         this.dirty = false;
11167         delete this.modified;
11168         this.editing = false;
11169         if(this.store){
11170             this.store.afterReject(this);
11171         }
11172     },
11173
11174     /**
11175      * Usually called by the {@link Roo.data.Store} which owns the Record.
11176      * Commits all changes made to the Record since either creation, or the last commit operation.
11177      * <p>
11178      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11179      * of commit operations.
11180      */
11181     commit : function(){
11182         this.dirty = false;
11183         delete this.modified;
11184         this.editing = false;
11185         if(this.store){
11186             this.store.afterCommit(this);
11187         }
11188     },
11189
11190     // private
11191     hasError : function(){
11192         return this.error != null;
11193     },
11194
11195     // private
11196     clearError : function(){
11197         this.error = null;
11198     },
11199
11200     /**
11201      * Creates a copy of this record.
11202      * @param {String} id (optional) A new record id if you don't want to use this record's id
11203      * @return {Record}
11204      */
11205     copy : function(newId) {
11206         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11207     }
11208 };/*
11209  * Based on:
11210  * Ext JS Library 1.1.1
11211  * Copyright(c) 2006-2007, Ext JS, LLC.
11212  *
11213  * Originally Released Under LGPL - original licence link has changed is not relivant.
11214  *
11215  * Fork - LGPL
11216  * <script type="text/javascript">
11217  */
11218
11219
11220
11221 /**
11222  * @class Roo.data.Store
11223  * @extends Roo.util.Observable
11224  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11225  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11226  * <p>
11227  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
11228  * has no knowledge of the format of the data returned by the Proxy.<br>
11229  * <p>
11230  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11231  * instances from the data object. These records are cached and made available through accessor functions.
11232  * @constructor
11233  * Creates a new Store.
11234  * @param {Object} config A config object containing the objects needed for the Store to access data,
11235  * and read the data into Records.
11236  */
11237 Roo.data.Store = function(config){
11238     this.data = new Roo.util.MixedCollection(false);
11239     this.data.getKey = function(o){
11240         return o.id;
11241     };
11242     this.baseParams = {};
11243     // private
11244     this.paramNames = {
11245         "start" : "start",
11246         "limit" : "limit",
11247         "sort" : "sort",
11248         "dir" : "dir",
11249         "multisort" : "_multisort"
11250     };
11251
11252     if(config && config.data){
11253         this.inlineData = config.data;
11254         delete config.data;
11255     }
11256
11257     Roo.apply(this, config);
11258     
11259     if(this.reader){ // reader passed
11260         this.reader = Roo.factory(this.reader, Roo.data);
11261         this.reader.xmodule = this.xmodule || false;
11262         if(!this.recordType){
11263             this.recordType = this.reader.recordType;
11264         }
11265         if(this.reader.onMetaChange){
11266             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11267         }
11268     }
11269
11270     if(this.recordType){
11271         this.fields = this.recordType.prototype.fields;
11272     }
11273     this.modified = [];
11274
11275     this.addEvents({
11276         /**
11277          * @event datachanged
11278          * Fires when the data cache has changed, and a widget which is using this Store
11279          * as a Record cache should refresh its view.
11280          * @param {Store} this
11281          */
11282         datachanged : true,
11283         /**
11284          * @event metachange
11285          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11286          * @param {Store} this
11287          * @param {Object} meta The JSON metadata
11288          */
11289         metachange : true,
11290         /**
11291          * @event add
11292          * Fires when Records have been added to the Store
11293          * @param {Store} this
11294          * @param {Roo.data.Record[]} records The array of Records added
11295          * @param {Number} index The index at which the record(s) were added
11296          */
11297         add : true,
11298         /**
11299          * @event remove
11300          * Fires when a Record has been removed from the Store
11301          * @param {Store} this
11302          * @param {Roo.data.Record} record The Record that was removed
11303          * @param {Number} index The index at which the record was removed
11304          */
11305         remove : true,
11306         /**
11307          * @event update
11308          * Fires when a Record has been updated
11309          * @param {Store} this
11310          * @param {Roo.data.Record} record The Record that was updated
11311          * @param {String} operation The update operation being performed.  Value may be one of:
11312          * <pre><code>
11313  Roo.data.Record.EDIT
11314  Roo.data.Record.REJECT
11315  Roo.data.Record.COMMIT
11316          * </code></pre>
11317          */
11318         update : true,
11319         /**
11320          * @event clear
11321          * Fires when the data cache has been cleared.
11322          * @param {Store} this
11323          */
11324         clear : true,
11325         /**
11326          * @event beforeload
11327          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11328          * the load action will be canceled.
11329          * @param {Store} this
11330          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11331          */
11332         beforeload : true,
11333         /**
11334          * @event beforeloadadd
11335          * Fires after a new set of Records has been loaded.
11336          * @param {Store} this
11337          * @param {Roo.data.Record[]} records The Records that were loaded
11338          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11339          */
11340         beforeloadadd : true,
11341         /**
11342          * @event load
11343          * Fires after a new set of Records has been loaded, before they are added to the store.
11344          * @param {Store} this
11345          * @param {Roo.data.Record[]} records The Records that were loaded
11346          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11347          * @params {Object} return from reader
11348          */
11349         load : true,
11350         /**
11351          * @event loadexception
11352          * Fires if an exception occurs in the Proxy during loading.
11353          * Called with the signature of the Proxy's "loadexception" event.
11354          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11355          * 
11356          * @param {Proxy} 
11357          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11358          * @param {Object} load options 
11359          * @param {Object} jsonData from your request (normally this contains the Exception)
11360          */
11361         loadexception : true
11362     });
11363     
11364     if(this.proxy){
11365         this.proxy = Roo.factory(this.proxy, Roo.data);
11366         this.proxy.xmodule = this.xmodule || false;
11367         this.relayEvents(this.proxy,  ["loadexception"]);
11368     }
11369     this.sortToggle = {};
11370     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11371
11372     Roo.data.Store.superclass.constructor.call(this);
11373
11374     if(this.inlineData){
11375         this.loadData(this.inlineData);
11376         delete this.inlineData;
11377     }
11378 };
11379
11380 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11381      /**
11382     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11383     * without a remote query - used by combo/forms at present.
11384     */
11385     
11386     /**
11387     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11388     */
11389     /**
11390     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11391     */
11392     /**
11393     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11394     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11395     */
11396     /**
11397     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11398     * on any HTTP request
11399     */
11400     /**
11401     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11402     */
11403     /**
11404     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11405     */
11406     multiSort: false,
11407     /**
11408     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11409     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11410     */
11411     remoteSort : false,
11412
11413     /**
11414     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11415      * loaded or when a record is removed. (defaults to false).
11416     */
11417     pruneModifiedRecords : false,
11418
11419     // private
11420     lastOptions : null,
11421
11422     /**
11423      * Add Records to the Store and fires the add event.
11424      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11425      */
11426     add : function(records){
11427         records = [].concat(records);
11428         for(var i = 0, len = records.length; i < len; i++){
11429             records[i].join(this);
11430         }
11431         var index = this.data.length;
11432         this.data.addAll(records);
11433         this.fireEvent("add", this, records, index);
11434     },
11435
11436     /**
11437      * Remove a Record from the Store and fires the remove event.
11438      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11439      */
11440     remove : function(record){
11441         var index = this.data.indexOf(record);
11442         this.data.removeAt(index);
11443  
11444         if(this.pruneModifiedRecords){
11445             this.modified.remove(record);
11446         }
11447         this.fireEvent("remove", this, record, index);
11448     },
11449
11450     /**
11451      * Remove all Records from the Store and fires the clear event.
11452      */
11453     removeAll : function(){
11454         this.data.clear();
11455         if(this.pruneModifiedRecords){
11456             this.modified = [];
11457         }
11458         this.fireEvent("clear", this);
11459     },
11460
11461     /**
11462      * Inserts Records to the Store at the given index and fires the add event.
11463      * @param {Number} index The start index at which to insert the passed Records.
11464      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11465      */
11466     insert : function(index, records){
11467         records = [].concat(records);
11468         for(var i = 0, len = records.length; i < len; i++){
11469             this.data.insert(index, records[i]);
11470             records[i].join(this);
11471         }
11472         this.fireEvent("add", this, records, index);
11473     },
11474
11475     /**
11476      * Get the index within the cache of the passed Record.
11477      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11478      * @return {Number} The index of the passed Record. Returns -1 if not found.
11479      */
11480     indexOf : function(record){
11481         return this.data.indexOf(record);
11482     },
11483
11484     /**
11485      * Get the index within the cache of the Record with the passed id.
11486      * @param {String} id The id of the Record to find.
11487      * @return {Number} The index of the Record. Returns -1 if not found.
11488      */
11489     indexOfId : function(id){
11490         return this.data.indexOfKey(id);
11491     },
11492
11493     /**
11494      * Get the Record with the specified id.
11495      * @param {String} id The id of the Record to find.
11496      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11497      */
11498     getById : function(id){
11499         return this.data.key(id);
11500     },
11501
11502     /**
11503      * Get the Record at the specified index.
11504      * @param {Number} index The index of the Record to find.
11505      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11506      */
11507     getAt : function(index){
11508         return this.data.itemAt(index);
11509     },
11510
11511     /**
11512      * Returns a range of Records between specified indices.
11513      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11514      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11515      * @return {Roo.data.Record[]} An array of Records
11516      */
11517     getRange : function(start, end){
11518         return this.data.getRange(start, end);
11519     },
11520
11521     // private
11522     storeOptions : function(o){
11523         o = Roo.apply({}, o);
11524         delete o.callback;
11525         delete o.scope;
11526         this.lastOptions = o;
11527     },
11528
11529     /**
11530      * Loads the Record cache from the configured Proxy using the configured Reader.
11531      * <p>
11532      * If using remote paging, then the first load call must specify the <em>start</em>
11533      * and <em>limit</em> properties in the options.params property to establish the initial
11534      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11535      * <p>
11536      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11537      * and this call will return before the new data has been loaded. Perform any post-processing
11538      * in a callback function, or in a "load" event handler.</strong>
11539      * <p>
11540      * @param {Object} options An object containing properties which control loading options:<ul>
11541      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11542      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11543      * passed the following arguments:<ul>
11544      * <li>r : Roo.data.Record[]</li>
11545      * <li>options: Options object from the load call</li>
11546      * <li>success: Boolean success indicator</li></ul></li>
11547      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11548      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11549      * </ul>
11550      */
11551     load : function(options){
11552         options = options || {};
11553         if(this.fireEvent("beforeload", this, options) !== false){
11554             this.storeOptions(options);
11555             var p = Roo.apply(options.params || {}, this.baseParams);
11556             // if meta was not loaded from remote source.. try requesting it.
11557             if (!this.reader.metaFromRemote) {
11558                 p._requestMeta = 1;
11559             }
11560             if(this.sortInfo && this.remoteSort){
11561                 var pn = this.paramNames;
11562                 p[pn["sort"]] = this.sortInfo.field;
11563                 p[pn["dir"]] = this.sortInfo.direction;
11564             }
11565             if (this.multiSort) {
11566                 var pn = this.paramNames;
11567                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11568             }
11569             
11570             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11571         }
11572     },
11573
11574     /**
11575      * Reloads the Record cache from the configured Proxy using the configured Reader and
11576      * the options from the last load operation performed.
11577      * @param {Object} options (optional) An object containing properties which may override the options
11578      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11579      * the most recently used options are reused).
11580      */
11581     reload : function(options){
11582         this.load(Roo.applyIf(options||{}, this.lastOptions));
11583     },
11584
11585     // private
11586     // Called as a callback by the Reader during a load operation.
11587     loadRecords : function(o, options, success){
11588         if(!o || success === false){
11589             if(success !== false){
11590                 this.fireEvent("load", this, [], options, o);
11591             }
11592             if(options.callback){
11593                 options.callback.call(options.scope || this, [], options, false);
11594             }
11595             return;
11596         }
11597         // if data returned failure - throw an exception.
11598         if (o.success === false) {
11599             // show a message if no listener is registered.
11600             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11601                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11602             }
11603             // loadmask wil be hooked into this..
11604             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11605             return;
11606         }
11607         var r = o.records, t = o.totalRecords || r.length;
11608         
11609         this.fireEvent("beforeloadadd", this, r, options, o);
11610         
11611         if(!options || options.add !== true){
11612             if(this.pruneModifiedRecords){
11613                 this.modified = [];
11614             }
11615             for(var i = 0, len = r.length; i < len; i++){
11616                 r[i].join(this);
11617             }
11618             if(this.snapshot){
11619                 this.data = this.snapshot;
11620                 delete this.snapshot;
11621             }
11622             this.data.clear();
11623             this.data.addAll(r);
11624             this.totalLength = t;
11625             this.applySort();
11626             this.fireEvent("datachanged", this);
11627         }else{
11628             this.totalLength = Math.max(t, this.data.length+r.length);
11629             this.add(r);
11630         }
11631         
11632         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11633                 
11634             var e = new Roo.data.Record({});
11635
11636             e.set(this.parent.displayField, this.parent.emptyTitle);
11637             e.set(this.parent.valueField, '');
11638
11639             this.insert(0, e);
11640         }
11641             
11642         this.fireEvent("load", this, r, options, o);
11643         if(options.callback){
11644             options.callback.call(options.scope || this, r, options, true);
11645         }
11646     },
11647
11648
11649     /**
11650      * Loads data from a passed data block. A Reader which understands the format of the data
11651      * must have been configured in the constructor.
11652      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11653      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11654      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11655      */
11656     loadData : function(o, append){
11657         var r = this.reader.readRecords(o);
11658         this.loadRecords(r, {add: append}, true);
11659     },
11660
11661     /**
11662      * Gets the number of cached records.
11663      * <p>
11664      * <em>If using paging, this may not be the total size of the dataset. If the data object
11665      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11666      * the data set size</em>
11667      */
11668     getCount : function(){
11669         return this.data.length || 0;
11670     },
11671
11672     /**
11673      * Gets the total number of records in the dataset as returned by the server.
11674      * <p>
11675      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11676      * the dataset size</em>
11677      */
11678     getTotalCount : function(){
11679         return this.totalLength || 0;
11680     },
11681
11682     /**
11683      * Returns the sort state of the Store as an object with two properties:
11684      * <pre><code>
11685  field {String} The name of the field by which the Records are sorted
11686  direction {String} The sort order, "ASC" or "DESC"
11687      * </code></pre>
11688      */
11689     getSortState : function(){
11690         return this.sortInfo;
11691     },
11692
11693     // private
11694     applySort : function(){
11695         if(this.sortInfo && !this.remoteSort){
11696             var s = this.sortInfo, f = s.field;
11697             var st = this.fields.get(f).sortType;
11698             var fn = function(r1, r2){
11699                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11700                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11701             };
11702             this.data.sort(s.direction, fn);
11703             if(this.snapshot && this.snapshot != this.data){
11704                 this.snapshot.sort(s.direction, fn);
11705             }
11706         }
11707     },
11708
11709     /**
11710      * Sets the default sort column and order to be used by the next load operation.
11711      * @param {String} fieldName The name of the field to sort by.
11712      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11713      */
11714     setDefaultSort : function(field, dir){
11715         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11716     },
11717
11718     /**
11719      * Sort the Records.
11720      * If remote sorting is used, the sort is performed on the server, and the cache is
11721      * reloaded. If local sorting is used, the cache is sorted internally.
11722      * @param {String} fieldName The name of the field to sort by.
11723      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11724      */
11725     sort : function(fieldName, dir){
11726         var f = this.fields.get(fieldName);
11727         if(!dir){
11728             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11729             
11730             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11731                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11732             }else{
11733                 dir = f.sortDir;
11734             }
11735         }
11736         this.sortToggle[f.name] = dir;
11737         this.sortInfo = {field: f.name, direction: dir};
11738         if(!this.remoteSort){
11739             this.applySort();
11740             this.fireEvent("datachanged", this);
11741         }else{
11742             this.load(this.lastOptions);
11743         }
11744     },
11745
11746     /**
11747      * Calls the specified function for each of the Records in the cache.
11748      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11749      * Returning <em>false</em> aborts and exits the iteration.
11750      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11751      */
11752     each : function(fn, scope){
11753         this.data.each(fn, scope);
11754     },
11755
11756     /**
11757      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11758      * (e.g., during paging).
11759      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11760      */
11761     getModifiedRecords : function(){
11762         return this.modified;
11763     },
11764
11765     // private
11766     createFilterFn : function(property, value, anyMatch){
11767         if(!value.exec){ // not a regex
11768             value = String(value);
11769             if(value.length == 0){
11770                 return false;
11771             }
11772             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11773         }
11774         return function(r){
11775             return value.test(r.data[property]);
11776         };
11777     },
11778
11779     /**
11780      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11781      * @param {String} property A field on your records
11782      * @param {Number} start The record index to start at (defaults to 0)
11783      * @param {Number} end The last record index to include (defaults to length - 1)
11784      * @return {Number} The sum
11785      */
11786     sum : function(property, start, end){
11787         var rs = this.data.items, v = 0;
11788         start = start || 0;
11789         end = (end || end === 0) ? end : rs.length-1;
11790
11791         for(var i = start; i <= end; i++){
11792             v += (rs[i].data[property] || 0);
11793         }
11794         return v;
11795     },
11796
11797     /**
11798      * Filter the records by a specified property.
11799      * @param {String} field A field on your records
11800      * @param {String/RegExp} value Either a string that the field
11801      * should start with or a RegExp to test against the field
11802      * @param {Boolean} anyMatch True to match any part not just the beginning
11803      */
11804     filter : function(property, value, anyMatch){
11805         var fn = this.createFilterFn(property, value, anyMatch);
11806         return fn ? this.filterBy(fn) : this.clearFilter();
11807     },
11808
11809     /**
11810      * Filter by a function. The specified function will be called with each
11811      * record in this data source. If the function returns true the record is included,
11812      * otherwise it is filtered.
11813      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11814      * @param {Object} scope (optional) The scope of the function (defaults to this)
11815      */
11816     filterBy : function(fn, scope){
11817         this.snapshot = this.snapshot || this.data;
11818         this.data = this.queryBy(fn, scope||this);
11819         this.fireEvent("datachanged", this);
11820     },
11821
11822     /**
11823      * Query the records by a specified property.
11824      * @param {String} field A field on your records
11825      * @param {String/RegExp} value Either a string that the field
11826      * should start with or a RegExp to test against the field
11827      * @param {Boolean} anyMatch True to match any part not just the beginning
11828      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11829      */
11830     query : function(property, value, anyMatch){
11831         var fn = this.createFilterFn(property, value, anyMatch);
11832         return fn ? this.queryBy(fn) : this.data.clone();
11833     },
11834
11835     /**
11836      * Query by a function. The specified function will be called with each
11837      * record in this data source. If the function returns true the record is included
11838      * in the results.
11839      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11840      * @param {Object} scope (optional) The scope of the function (defaults to this)
11841       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11842      **/
11843     queryBy : function(fn, scope){
11844         var data = this.snapshot || this.data;
11845         return data.filterBy(fn, scope||this);
11846     },
11847
11848     /**
11849      * Collects unique values for a particular dataIndex from this store.
11850      * @param {String} dataIndex The property to collect
11851      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11852      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11853      * @return {Array} An array of the unique values
11854      **/
11855     collect : function(dataIndex, allowNull, bypassFilter){
11856         var d = (bypassFilter === true && this.snapshot) ?
11857                 this.snapshot.items : this.data.items;
11858         var v, sv, r = [], l = {};
11859         for(var i = 0, len = d.length; i < len; i++){
11860             v = d[i].data[dataIndex];
11861             sv = String(v);
11862             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11863                 l[sv] = true;
11864                 r[r.length] = v;
11865             }
11866         }
11867         return r;
11868     },
11869
11870     /**
11871      * Revert to a view of the Record cache with no filtering applied.
11872      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11873      */
11874     clearFilter : function(suppressEvent){
11875         if(this.snapshot && this.snapshot != this.data){
11876             this.data = this.snapshot;
11877             delete this.snapshot;
11878             if(suppressEvent !== true){
11879                 this.fireEvent("datachanged", this);
11880             }
11881         }
11882     },
11883
11884     // private
11885     afterEdit : function(record){
11886         if(this.modified.indexOf(record) == -1){
11887             this.modified.push(record);
11888         }
11889         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11890     },
11891     
11892     // private
11893     afterReject : function(record){
11894         this.modified.remove(record);
11895         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11896     },
11897
11898     // private
11899     afterCommit : function(record){
11900         this.modified.remove(record);
11901         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11902     },
11903
11904     /**
11905      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11906      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11907      */
11908     commitChanges : function(){
11909         var m = this.modified.slice(0);
11910         this.modified = [];
11911         for(var i = 0, len = m.length; i < len; i++){
11912             m[i].commit();
11913         }
11914     },
11915
11916     /**
11917      * Cancel outstanding changes on all changed records.
11918      */
11919     rejectChanges : function(){
11920         var m = this.modified.slice(0);
11921         this.modified = [];
11922         for(var i = 0, len = m.length; i < len; i++){
11923             m[i].reject();
11924         }
11925     },
11926
11927     onMetaChange : function(meta, rtype, o){
11928         this.recordType = rtype;
11929         this.fields = rtype.prototype.fields;
11930         delete this.snapshot;
11931         this.sortInfo = meta.sortInfo || this.sortInfo;
11932         this.modified = [];
11933         this.fireEvent('metachange', this, this.reader.meta);
11934     },
11935     
11936     moveIndex : function(data, type)
11937     {
11938         var index = this.indexOf(data);
11939         
11940         var newIndex = index + type;
11941         
11942         this.remove(data);
11943         
11944         this.insert(newIndex, data);
11945         
11946     }
11947 });/*
11948  * Based on:
11949  * Ext JS Library 1.1.1
11950  * Copyright(c) 2006-2007, Ext JS, LLC.
11951  *
11952  * Originally Released Under LGPL - original licence link has changed is not relivant.
11953  *
11954  * Fork - LGPL
11955  * <script type="text/javascript">
11956  */
11957
11958 /**
11959  * @class Roo.data.SimpleStore
11960  * @extends Roo.data.Store
11961  * Small helper class to make creating Stores from Array data easier.
11962  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11963  * @cfg {Array} fields An array of field definition objects, or field name strings.
11964  * @cfg {Array} data The multi-dimensional array of data
11965  * @constructor
11966  * @param {Object} config
11967  */
11968 Roo.data.SimpleStore = function(config){
11969     Roo.data.SimpleStore.superclass.constructor.call(this, {
11970         isLocal : true,
11971         reader: new Roo.data.ArrayReader({
11972                 id: config.id
11973             },
11974             Roo.data.Record.create(config.fields)
11975         ),
11976         proxy : new Roo.data.MemoryProxy(config.data)
11977     });
11978     this.load();
11979 };
11980 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990
11991 /**
11992 /**
11993  * @extends Roo.data.Store
11994  * @class Roo.data.JsonStore
11995  * Small helper class to make creating Stores for JSON data easier. <br/>
11996 <pre><code>
11997 var store = new Roo.data.JsonStore({
11998     url: 'get-images.php',
11999     root: 'images',
12000     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12001 });
12002 </code></pre>
12003  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12004  * JsonReader and HttpProxy (unless inline data is provided).</b>
12005  * @cfg {Array} fields An array of field definition objects, or field name strings.
12006  * @constructor
12007  * @param {Object} config
12008  */
12009 Roo.data.JsonStore = function(c){
12010     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12011         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12012         reader: new Roo.data.JsonReader(c, c.fields)
12013     }));
12014 };
12015 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12016  * Based on:
12017  * Ext JS Library 1.1.1
12018  * Copyright(c) 2006-2007, Ext JS, LLC.
12019  *
12020  * Originally Released Under LGPL - original licence link has changed is not relivant.
12021  *
12022  * Fork - LGPL
12023  * <script type="text/javascript">
12024  */
12025
12026  
12027 Roo.data.Field = function(config){
12028     if(typeof config == "string"){
12029         config = {name: config};
12030     }
12031     Roo.apply(this, config);
12032     
12033     if(!this.type){
12034         this.type = "auto";
12035     }
12036     
12037     var st = Roo.data.SortTypes;
12038     // named sortTypes are supported, here we look them up
12039     if(typeof this.sortType == "string"){
12040         this.sortType = st[this.sortType];
12041     }
12042     
12043     // set default sortType for strings and dates
12044     if(!this.sortType){
12045         switch(this.type){
12046             case "string":
12047                 this.sortType = st.asUCString;
12048                 break;
12049             case "date":
12050                 this.sortType = st.asDate;
12051                 break;
12052             default:
12053                 this.sortType = st.none;
12054         }
12055     }
12056
12057     // define once
12058     var stripRe = /[\$,%]/g;
12059
12060     // prebuilt conversion function for this field, instead of
12061     // switching every time we're reading a value
12062     if(!this.convert){
12063         var cv, dateFormat = this.dateFormat;
12064         switch(this.type){
12065             case "":
12066             case "auto":
12067             case undefined:
12068                 cv = function(v){ return v; };
12069                 break;
12070             case "string":
12071                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12072                 break;
12073             case "int":
12074                 cv = function(v){
12075                     return v !== undefined && v !== null && v !== '' ?
12076                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12077                     };
12078                 break;
12079             case "float":
12080                 cv = function(v){
12081                     return v !== undefined && v !== null && v !== '' ?
12082                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12083                     };
12084                 break;
12085             case "bool":
12086             case "boolean":
12087                 cv = function(v){ return v === true || v === "true" || v == 1; };
12088                 break;
12089             case "date":
12090                 cv = function(v){
12091                     if(!v){
12092                         return '';
12093                     }
12094                     if(v instanceof Date){
12095                         return v;
12096                     }
12097                     if(dateFormat){
12098                         if(dateFormat == "timestamp"){
12099                             return new Date(v*1000);
12100                         }
12101                         return Date.parseDate(v, dateFormat);
12102                     }
12103                     var parsed = Date.parse(v);
12104                     return parsed ? new Date(parsed) : null;
12105                 };
12106              break;
12107             
12108         }
12109         this.convert = cv;
12110     }
12111 };
12112
12113 Roo.data.Field.prototype = {
12114     dateFormat: null,
12115     defaultValue: "",
12116     mapping: null,
12117     sortType : null,
12118     sortDir : "ASC"
12119 };/*
12120  * Based on:
12121  * Ext JS Library 1.1.1
12122  * Copyright(c) 2006-2007, Ext JS, LLC.
12123  *
12124  * Originally Released Under LGPL - original licence link has changed is not relivant.
12125  *
12126  * Fork - LGPL
12127  * <script type="text/javascript">
12128  */
12129  
12130 // Base class for reading structured data from a data source.  This class is intended to be
12131 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12132
12133 /**
12134  * @class Roo.data.DataReader
12135  * Base class for reading structured data from a data source.  This class is intended to be
12136  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12137  */
12138
12139 Roo.data.DataReader = function(meta, recordType){
12140     
12141     this.meta = meta;
12142     
12143     this.recordType = recordType instanceof Array ? 
12144         Roo.data.Record.create(recordType) : recordType;
12145 };
12146
12147 Roo.data.DataReader.prototype = {
12148      /**
12149      * Create an empty record
12150      * @param {Object} data (optional) - overlay some values
12151      * @return {Roo.data.Record} record created.
12152      */
12153     newRow :  function(d) {
12154         var da =  {};
12155         this.recordType.prototype.fields.each(function(c) {
12156             switch( c.type) {
12157                 case 'int' : da[c.name] = 0; break;
12158                 case 'date' : da[c.name] = new Date(); break;
12159                 case 'float' : da[c.name] = 0.0; break;
12160                 case 'boolean' : da[c.name] = false; break;
12161                 default : da[c.name] = ""; break;
12162             }
12163             
12164         });
12165         return new this.recordType(Roo.apply(da, d));
12166     }
12167     
12168 };/*
12169  * Based on:
12170  * Ext JS Library 1.1.1
12171  * Copyright(c) 2006-2007, Ext JS, LLC.
12172  *
12173  * Originally Released Under LGPL - original licence link has changed is not relivant.
12174  *
12175  * Fork - LGPL
12176  * <script type="text/javascript">
12177  */
12178
12179 /**
12180  * @class Roo.data.DataProxy
12181  * @extends Roo.data.Observable
12182  * This class is an abstract base class for implementations which provide retrieval of
12183  * unformatted data objects.<br>
12184  * <p>
12185  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12186  * (of the appropriate type which knows how to parse the data object) to provide a block of
12187  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12188  * <p>
12189  * Custom implementations must implement the load method as described in
12190  * {@link Roo.data.HttpProxy#load}.
12191  */
12192 Roo.data.DataProxy = function(){
12193     this.addEvents({
12194         /**
12195          * @event beforeload
12196          * Fires before a network request is made to retrieve a data object.
12197          * @param {Object} This DataProxy object.
12198          * @param {Object} params The params parameter to the load function.
12199          */
12200         beforeload : true,
12201         /**
12202          * @event load
12203          * Fires before the load method's callback is called.
12204          * @param {Object} This DataProxy object.
12205          * @param {Object} o The data object.
12206          * @param {Object} arg The callback argument object passed to the load function.
12207          */
12208         load : true,
12209         /**
12210          * @event loadexception
12211          * Fires if an Exception occurs during data retrieval.
12212          * @param {Object} This DataProxy object.
12213          * @param {Object} o The data object.
12214          * @param {Object} arg The callback argument object passed to the load function.
12215          * @param {Object} e The Exception.
12216          */
12217         loadexception : true
12218     });
12219     Roo.data.DataProxy.superclass.constructor.call(this);
12220 };
12221
12222 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12223
12224     /**
12225      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12226      */
12227 /*
12228  * Based on:
12229  * Ext JS Library 1.1.1
12230  * Copyright(c) 2006-2007, Ext JS, LLC.
12231  *
12232  * Originally Released Under LGPL - original licence link has changed is not relivant.
12233  *
12234  * Fork - LGPL
12235  * <script type="text/javascript">
12236  */
12237 /**
12238  * @class Roo.data.MemoryProxy
12239  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12240  * to the Reader when its load method is called.
12241  * @constructor
12242  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12243  */
12244 Roo.data.MemoryProxy = function(data){
12245     if (data.data) {
12246         data = data.data;
12247     }
12248     Roo.data.MemoryProxy.superclass.constructor.call(this);
12249     this.data = data;
12250 };
12251
12252 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12253     
12254     /**
12255      * Load data from the requested source (in this case an in-memory
12256      * data object passed to the constructor), read the data object into
12257      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12258      * process that block using the passed callback.
12259      * @param {Object} params This parameter is not used by the MemoryProxy class.
12260      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12261      * object into a block of Roo.data.Records.
12262      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12263      * The function must be passed <ul>
12264      * <li>The Record block object</li>
12265      * <li>The "arg" argument from the load function</li>
12266      * <li>A boolean success indicator</li>
12267      * </ul>
12268      * @param {Object} scope The scope in which to call the callback
12269      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12270      */
12271     load : function(params, reader, callback, scope, arg){
12272         params = params || {};
12273         var result;
12274         try {
12275             result = reader.readRecords(this.data);
12276         }catch(e){
12277             this.fireEvent("loadexception", this, arg, null, e);
12278             callback.call(scope, null, arg, false);
12279             return;
12280         }
12281         callback.call(scope, result, arg, true);
12282     },
12283     
12284     // private
12285     update : function(params, records){
12286         
12287     }
12288 });/*
12289  * Based on:
12290  * Ext JS Library 1.1.1
12291  * Copyright(c) 2006-2007, Ext JS, LLC.
12292  *
12293  * Originally Released Under LGPL - original licence link has changed is not relivant.
12294  *
12295  * Fork - LGPL
12296  * <script type="text/javascript">
12297  */
12298 /**
12299  * @class Roo.data.HttpProxy
12300  * @extends Roo.data.DataProxy
12301  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12302  * configured to reference a certain URL.<br><br>
12303  * <p>
12304  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12305  * from which the running page was served.<br><br>
12306  * <p>
12307  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12308  * <p>
12309  * Be aware that to enable the browser to parse an XML document, the server must set
12310  * the Content-Type header in the HTTP response to "text/xml".
12311  * @constructor
12312  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12313  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12314  * will be used to make the request.
12315  */
12316 Roo.data.HttpProxy = function(conn){
12317     Roo.data.HttpProxy.superclass.constructor.call(this);
12318     // is conn a conn config or a real conn?
12319     this.conn = conn;
12320     this.useAjax = !conn || !conn.events;
12321   
12322 };
12323
12324 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12325     // thse are take from connection...
12326     
12327     /**
12328      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12329      */
12330     /**
12331      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12332      * extra parameters to each request made by this object. (defaults to undefined)
12333      */
12334     /**
12335      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12336      *  to each request made by this object. (defaults to undefined)
12337      */
12338     /**
12339      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12340      */
12341     /**
12342      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12343      */
12344      /**
12345      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12346      * @type Boolean
12347      */
12348   
12349
12350     /**
12351      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12352      * @type Boolean
12353      */
12354     /**
12355      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12356      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12357      * a finer-grained basis than the DataProxy events.
12358      */
12359     getConnection : function(){
12360         return this.useAjax ? Roo.Ajax : this.conn;
12361     },
12362
12363     /**
12364      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12365      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12366      * process that block using the passed callback.
12367      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12368      * for the request to the remote server.
12369      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12370      * object into a block of Roo.data.Records.
12371      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12372      * The function must be passed <ul>
12373      * <li>The Record block object</li>
12374      * <li>The "arg" argument from the load function</li>
12375      * <li>A boolean success indicator</li>
12376      * </ul>
12377      * @param {Object} scope The scope in which to call the callback
12378      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12379      */
12380     load : function(params, reader, callback, scope, arg){
12381         if(this.fireEvent("beforeload", this, params) !== false){
12382             var  o = {
12383                 params : params || {},
12384                 request: {
12385                     callback : callback,
12386                     scope : scope,
12387                     arg : arg
12388                 },
12389                 reader: reader,
12390                 callback : this.loadResponse,
12391                 scope: this
12392             };
12393             if(this.useAjax){
12394                 Roo.applyIf(o, this.conn);
12395                 if(this.activeRequest){
12396                     Roo.Ajax.abort(this.activeRequest);
12397                 }
12398                 this.activeRequest = Roo.Ajax.request(o);
12399             }else{
12400                 this.conn.request(o);
12401             }
12402         }else{
12403             callback.call(scope||this, null, arg, false);
12404         }
12405     },
12406
12407     // private
12408     loadResponse : function(o, success, response){
12409         delete this.activeRequest;
12410         if(!success){
12411             this.fireEvent("loadexception", this, o, response);
12412             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12413             return;
12414         }
12415         var result;
12416         try {
12417             result = o.reader.read(response);
12418         }catch(e){
12419             this.fireEvent("loadexception", this, o, response, e);
12420             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12421             return;
12422         }
12423         
12424         this.fireEvent("load", this, o, o.request.arg);
12425         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12426     },
12427
12428     // private
12429     update : function(dataSet){
12430
12431     },
12432
12433     // private
12434     updateResponse : function(dataSet){
12435
12436     }
12437 });/*
12438  * Based on:
12439  * Ext JS Library 1.1.1
12440  * Copyright(c) 2006-2007, Ext JS, LLC.
12441  *
12442  * Originally Released Under LGPL - original licence link has changed is not relivant.
12443  *
12444  * Fork - LGPL
12445  * <script type="text/javascript">
12446  */
12447
12448 /**
12449  * @class Roo.data.ScriptTagProxy
12450  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12451  * other than the originating domain of the running page.<br><br>
12452  * <p>
12453  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12454  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12455  * <p>
12456  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12457  * source code that is used as the source inside a &lt;script> tag.<br><br>
12458  * <p>
12459  * In order for the browser to process the returned data, the server must wrap the data object
12460  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12461  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12462  * depending on whether the callback name was passed:
12463  * <p>
12464  * <pre><code>
12465 boolean scriptTag = false;
12466 String cb = request.getParameter("callback");
12467 if (cb != null) {
12468     scriptTag = true;
12469     response.setContentType("text/javascript");
12470 } else {
12471     response.setContentType("application/x-json");
12472 }
12473 Writer out = response.getWriter();
12474 if (scriptTag) {
12475     out.write(cb + "(");
12476 }
12477 out.print(dataBlock.toJsonString());
12478 if (scriptTag) {
12479     out.write(");");
12480 }
12481 </pre></code>
12482  *
12483  * @constructor
12484  * @param {Object} config A configuration object.
12485  */
12486 Roo.data.ScriptTagProxy = function(config){
12487     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12488     Roo.apply(this, config);
12489     this.head = document.getElementsByTagName("head")[0];
12490 };
12491
12492 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12493
12494 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12495     /**
12496      * @cfg {String} url The URL from which to request the data object.
12497      */
12498     /**
12499      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12500      */
12501     timeout : 30000,
12502     /**
12503      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12504      * the server the name of the callback function set up by the load call to process the returned data object.
12505      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12506      * javascript output which calls this named function passing the data object as its only parameter.
12507      */
12508     callbackParam : "callback",
12509     /**
12510      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12511      * name to the request.
12512      */
12513     nocache : true,
12514
12515     /**
12516      * Load data from the configured URL, read the data object into
12517      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12518      * process that block using the passed callback.
12519      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12520      * for the request to the remote server.
12521      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12522      * object into a block of Roo.data.Records.
12523      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12524      * The function must be passed <ul>
12525      * <li>The Record block object</li>
12526      * <li>The "arg" argument from the load function</li>
12527      * <li>A boolean success indicator</li>
12528      * </ul>
12529      * @param {Object} scope The scope in which to call the callback
12530      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12531      */
12532     load : function(params, reader, callback, scope, arg){
12533         if(this.fireEvent("beforeload", this, params) !== false){
12534
12535             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12536
12537             var url = this.url;
12538             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12539             if(this.nocache){
12540                 url += "&_dc=" + (new Date().getTime());
12541             }
12542             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12543             var trans = {
12544                 id : transId,
12545                 cb : "stcCallback"+transId,
12546                 scriptId : "stcScript"+transId,
12547                 params : params,
12548                 arg : arg,
12549                 url : url,
12550                 callback : callback,
12551                 scope : scope,
12552                 reader : reader
12553             };
12554             var conn = this;
12555
12556             window[trans.cb] = function(o){
12557                 conn.handleResponse(o, trans);
12558             };
12559
12560             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12561
12562             if(this.autoAbort !== false){
12563                 this.abort();
12564             }
12565
12566             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12567
12568             var script = document.createElement("script");
12569             script.setAttribute("src", url);
12570             script.setAttribute("type", "text/javascript");
12571             script.setAttribute("id", trans.scriptId);
12572             this.head.appendChild(script);
12573
12574             this.trans = trans;
12575         }else{
12576             callback.call(scope||this, null, arg, false);
12577         }
12578     },
12579
12580     // private
12581     isLoading : function(){
12582         return this.trans ? true : false;
12583     },
12584
12585     /**
12586      * Abort the current server request.
12587      */
12588     abort : function(){
12589         if(this.isLoading()){
12590             this.destroyTrans(this.trans);
12591         }
12592     },
12593
12594     // private
12595     destroyTrans : function(trans, isLoaded){
12596         this.head.removeChild(document.getElementById(trans.scriptId));
12597         clearTimeout(trans.timeoutId);
12598         if(isLoaded){
12599             window[trans.cb] = undefined;
12600             try{
12601                 delete window[trans.cb];
12602             }catch(e){}
12603         }else{
12604             // if hasn't been loaded, wait for load to remove it to prevent script error
12605             window[trans.cb] = function(){
12606                 window[trans.cb] = undefined;
12607                 try{
12608                     delete window[trans.cb];
12609                 }catch(e){}
12610             };
12611         }
12612     },
12613
12614     // private
12615     handleResponse : function(o, trans){
12616         this.trans = false;
12617         this.destroyTrans(trans, true);
12618         var result;
12619         try {
12620             result = trans.reader.readRecords(o);
12621         }catch(e){
12622             this.fireEvent("loadexception", this, o, trans.arg, e);
12623             trans.callback.call(trans.scope||window, null, trans.arg, false);
12624             return;
12625         }
12626         this.fireEvent("load", this, o, trans.arg);
12627         trans.callback.call(trans.scope||window, result, trans.arg, true);
12628     },
12629
12630     // private
12631     handleFailure : function(trans){
12632         this.trans = false;
12633         this.destroyTrans(trans, false);
12634         this.fireEvent("loadexception", this, null, trans.arg);
12635         trans.callback.call(trans.scope||window, null, trans.arg, false);
12636     }
12637 });/*
12638  * Based on:
12639  * Ext JS Library 1.1.1
12640  * Copyright(c) 2006-2007, Ext JS, LLC.
12641  *
12642  * Originally Released Under LGPL - original licence link has changed is not relivant.
12643  *
12644  * Fork - LGPL
12645  * <script type="text/javascript">
12646  */
12647
12648 /**
12649  * @class Roo.data.JsonReader
12650  * @extends Roo.data.DataReader
12651  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12652  * based on mappings in a provided Roo.data.Record constructor.
12653  * 
12654  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12655  * in the reply previously. 
12656  * 
12657  * <p>
12658  * Example code:
12659  * <pre><code>
12660 var RecordDef = Roo.data.Record.create([
12661     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12662     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12663 ]);
12664 var myReader = new Roo.data.JsonReader({
12665     totalProperty: "results",    // The property which contains the total dataset size (optional)
12666     root: "rows",                // The property which contains an Array of row objects
12667     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12668 }, RecordDef);
12669 </code></pre>
12670  * <p>
12671  * This would consume a JSON file like this:
12672  * <pre><code>
12673 { 'results': 2, 'rows': [
12674     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12675     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12676 }
12677 </code></pre>
12678  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12679  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12680  * paged from the remote server.
12681  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12682  * @cfg {String} root name of the property which contains the Array of row objects.
12683  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12684  * @cfg {Array} fields Array of field definition objects
12685  * @constructor
12686  * Create a new JsonReader
12687  * @param {Object} meta Metadata configuration options
12688  * @param {Object} recordType Either an Array of field definition objects,
12689  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12690  */
12691 Roo.data.JsonReader = function(meta, recordType){
12692     
12693     meta = meta || {};
12694     // set some defaults:
12695     Roo.applyIf(meta, {
12696         totalProperty: 'total',
12697         successProperty : 'success',
12698         root : 'data',
12699         id : 'id'
12700     });
12701     
12702     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12703 };
12704 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12705     
12706     /**
12707      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12708      * Used by Store query builder to append _requestMeta to params.
12709      * 
12710      */
12711     metaFromRemote : false,
12712     /**
12713      * This method is only used by a DataProxy which has retrieved data from a remote server.
12714      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12715      * @return {Object} data A data block which is used by an Roo.data.Store object as
12716      * a cache of Roo.data.Records.
12717      */
12718     read : function(response){
12719         var json = response.responseText;
12720        
12721         var o = /* eval:var:o */ eval("("+json+")");
12722         if(!o) {
12723             throw {message: "JsonReader.read: Json object not found"};
12724         }
12725         
12726         if(o.metaData){
12727             
12728             delete this.ef;
12729             this.metaFromRemote = true;
12730             this.meta = o.metaData;
12731             this.recordType = Roo.data.Record.create(o.metaData.fields);
12732             this.onMetaChange(this.meta, this.recordType, o);
12733         }
12734         return this.readRecords(o);
12735     },
12736
12737     // private function a store will implement
12738     onMetaChange : function(meta, recordType, o){
12739
12740     },
12741
12742     /**
12743          * @ignore
12744          */
12745     simpleAccess: function(obj, subsc) {
12746         return obj[subsc];
12747     },
12748
12749         /**
12750          * @ignore
12751          */
12752     getJsonAccessor: function(){
12753         var re = /[\[\.]/;
12754         return function(expr) {
12755             try {
12756                 return(re.test(expr))
12757                     ? new Function("obj", "return obj." + expr)
12758                     : function(obj){
12759                         return obj[expr];
12760                     };
12761             } catch(e){}
12762             return Roo.emptyFn;
12763         };
12764     }(),
12765
12766     /**
12767      * Create a data block containing Roo.data.Records from an XML document.
12768      * @param {Object} o An object which contains an Array of row objects in the property specified
12769      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12770      * which contains the total size of the dataset.
12771      * @return {Object} data A data block which is used by an Roo.data.Store object as
12772      * a cache of Roo.data.Records.
12773      */
12774     readRecords : function(o){
12775         /**
12776          * After any data loads, the raw JSON data is available for further custom processing.
12777          * @type Object
12778          */
12779         this.o = o;
12780         var s = this.meta, Record = this.recordType,
12781             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12782
12783 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12784         if (!this.ef) {
12785             if(s.totalProperty) {
12786                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12787                 }
12788                 if(s.successProperty) {
12789                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12790                 }
12791                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12792                 if (s.id) {
12793                         var g = this.getJsonAccessor(s.id);
12794                         this.getId = function(rec) {
12795                                 var r = g(rec);  
12796                                 return (r === undefined || r === "") ? null : r;
12797                         };
12798                 } else {
12799                         this.getId = function(){return null;};
12800                 }
12801             this.ef = [];
12802             for(var jj = 0; jj < fl; jj++){
12803                 f = fi[jj];
12804                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12805                 this.ef[jj] = this.getJsonAccessor(map);
12806             }
12807         }
12808
12809         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12810         if(s.totalProperty){
12811             var vt = parseInt(this.getTotal(o), 10);
12812             if(!isNaN(vt)){
12813                 totalRecords = vt;
12814             }
12815         }
12816         if(s.successProperty){
12817             var vs = this.getSuccess(o);
12818             if(vs === false || vs === 'false'){
12819                 success = false;
12820             }
12821         }
12822         var records = [];
12823         for(var i = 0; i < c; i++){
12824                 var n = root[i];
12825             var values = {};
12826             var id = this.getId(n);
12827             for(var j = 0; j < fl; j++){
12828                 f = fi[j];
12829             var v = this.ef[j](n);
12830             if (!f.convert) {
12831                 Roo.log('missing convert for ' + f.name);
12832                 Roo.log(f);
12833                 continue;
12834             }
12835             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12836             }
12837             var record = new Record(values, id);
12838             record.json = n;
12839             records[i] = record;
12840         }
12841         return {
12842             raw : o,
12843             success : success,
12844             records : records,
12845             totalRecords : totalRecords
12846         };
12847     }
12848 });/*
12849  * Based on:
12850  * Ext JS Library 1.1.1
12851  * Copyright(c) 2006-2007, Ext JS, LLC.
12852  *
12853  * Originally Released Under LGPL - original licence link has changed is not relivant.
12854  *
12855  * Fork - LGPL
12856  * <script type="text/javascript">
12857  */
12858
12859 /**
12860  * @class Roo.data.ArrayReader
12861  * @extends Roo.data.DataReader
12862  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12863  * Each element of that Array represents a row of data fields. The
12864  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12865  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12866  * <p>
12867  * Example code:.
12868  * <pre><code>
12869 var RecordDef = Roo.data.Record.create([
12870     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12871     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12872 ]);
12873 var myReader = new Roo.data.ArrayReader({
12874     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12875 }, RecordDef);
12876 </code></pre>
12877  * <p>
12878  * This would consume an Array like this:
12879  * <pre><code>
12880 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12881   </code></pre>
12882  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12883  * @constructor
12884  * Create a new JsonReader
12885  * @param {Object} meta Metadata configuration options.
12886  * @param {Object} recordType Either an Array of field definition objects
12887  * as specified to {@link Roo.data.Record#create},
12888  * or an {@link Roo.data.Record} object
12889  * created using {@link Roo.data.Record#create}.
12890  */
12891 Roo.data.ArrayReader = function(meta, recordType){
12892     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12893 };
12894
12895 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12896     /**
12897      * Create a data block containing Roo.data.Records from an XML document.
12898      * @param {Object} o An Array of row objects which represents the dataset.
12899      * @return {Object} data A data block which is used by an Roo.data.Store object as
12900      * a cache of Roo.data.Records.
12901      */
12902     readRecords : function(o){
12903         var sid = this.meta ? this.meta.id : null;
12904         var recordType = this.recordType, fields = recordType.prototype.fields;
12905         var records = [];
12906         var root = o;
12907             for(var i = 0; i < root.length; i++){
12908                     var n = root[i];
12909                 var values = {};
12910                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12911                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12912                 var f = fields.items[j];
12913                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12914                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12915                 v = f.convert(v);
12916                 values[f.name] = v;
12917             }
12918                 var record = new recordType(values, id);
12919                 record.json = n;
12920                 records[records.length] = record;
12921             }
12922             return {
12923                 records : records,
12924                 totalRecords : records.length
12925             };
12926     }
12927 });/*
12928  * - LGPL
12929  * * 
12930  */
12931
12932 /**
12933  * @class Roo.bootstrap.ComboBox
12934  * @extends Roo.bootstrap.TriggerField
12935  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12936  * @cfg {Boolean} append (true|false) default false
12937  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12938  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12939  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12940  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12941  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12942  * @cfg {Boolean} animate default true
12943  * @cfg {Boolean} emptyResultText only for touch device
12944  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12945  * @cfg {String} emptyTitle default ''
12946  * @constructor
12947  * Create a new ComboBox.
12948  * @param {Object} config Configuration options
12949  */
12950 Roo.bootstrap.ComboBox = function(config){
12951     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12952     this.addEvents({
12953         /**
12954          * @event expand
12955          * Fires when the dropdown list is expanded
12956         * @param {Roo.bootstrap.ComboBox} combo This combo box
12957         */
12958         'expand' : true,
12959         /**
12960          * @event collapse
12961          * Fires when the dropdown list is collapsed
12962         * @param {Roo.bootstrap.ComboBox} combo This combo box
12963         */
12964         'collapse' : true,
12965         /**
12966          * @event beforeselect
12967          * Fires before a list item is selected. Return false to cancel the selection.
12968         * @param {Roo.bootstrap.ComboBox} combo This combo box
12969         * @param {Roo.data.Record} record The data record returned from the underlying store
12970         * @param {Number} index The index of the selected item in the dropdown list
12971         */
12972         'beforeselect' : true,
12973         /**
12974          * @event select
12975          * Fires when a list item is selected
12976         * @param {Roo.bootstrap.ComboBox} combo This combo box
12977         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12978         * @param {Number} index The index of the selected item in the dropdown list
12979         */
12980         'select' : true,
12981         /**
12982          * @event beforequery
12983          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12984          * The event object passed has these properties:
12985         * @param {Roo.bootstrap.ComboBox} combo This combo box
12986         * @param {String} query The query
12987         * @param {Boolean} forceAll true to force "all" query
12988         * @param {Boolean} cancel true to cancel the query
12989         * @param {Object} e The query event object
12990         */
12991         'beforequery': true,
12992          /**
12993          * @event add
12994          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12995         * @param {Roo.bootstrap.ComboBox} combo This combo box
12996         */
12997         'add' : true,
12998         /**
12999          * @event edit
13000          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13001         * @param {Roo.bootstrap.ComboBox} combo This combo box
13002         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13003         */
13004         'edit' : true,
13005         /**
13006          * @event remove
13007          * Fires when the remove value from the combobox array
13008         * @param {Roo.bootstrap.ComboBox} combo This combo box
13009         */
13010         'remove' : true,
13011         /**
13012          * @event afterremove
13013          * Fires when the remove value from the combobox array
13014         * @param {Roo.bootstrap.ComboBox} combo This combo box
13015         */
13016         'afterremove' : true,
13017         /**
13018          * @event specialfilter
13019          * Fires when specialfilter
13020             * @param {Roo.bootstrap.ComboBox} combo This combo box
13021             */
13022         'specialfilter' : true,
13023         /**
13024          * @event tick
13025          * Fires when tick the element
13026             * @param {Roo.bootstrap.ComboBox} combo This combo box
13027             */
13028         'tick' : true,
13029         /**
13030          * @event touchviewdisplay
13031          * Fires when touch view require special display (default is using displayField)
13032             * @param {Roo.bootstrap.ComboBox} combo This combo box
13033             * @param {Object} cfg set html .
13034             */
13035         'touchviewdisplay' : true
13036         
13037     });
13038     
13039     this.item = [];
13040     this.tickItems = [];
13041     
13042     this.selectedIndex = -1;
13043     if(this.mode == 'local'){
13044         if(config.queryDelay === undefined){
13045             this.queryDelay = 10;
13046         }
13047         if(config.minChars === undefined){
13048             this.minChars = 0;
13049         }
13050     }
13051 };
13052
13053 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13054      
13055     /**
13056      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13057      * rendering into an Roo.Editor, defaults to false)
13058      */
13059     /**
13060      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13061      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13062      */
13063     /**
13064      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13065      */
13066     /**
13067      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13068      * the dropdown list (defaults to undefined, with no header element)
13069      */
13070
13071      /**
13072      * @cfg {String/Roo.Template} tpl The template to use to render the output
13073      */
13074      
13075      /**
13076      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13077      */
13078     listWidth: undefined,
13079     /**
13080      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13081      * mode = 'remote' or 'text' if mode = 'local')
13082      */
13083     displayField: undefined,
13084     
13085     /**
13086      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13087      * mode = 'remote' or 'value' if mode = 'local'). 
13088      * Note: use of a valueField requires the user make a selection
13089      * in order for a value to be mapped.
13090      */
13091     valueField: undefined,
13092     /**
13093      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13094      */
13095     modalTitle : '',
13096     
13097     /**
13098      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13099      * field's data value (defaults to the underlying DOM element's name)
13100      */
13101     hiddenName: undefined,
13102     /**
13103      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13104      */
13105     listClass: '',
13106     /**
13107      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13108      */
13109     selectedClass: 'active',
13110     
13111     /**
13112      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13113      */
13114     shadow:'sides',
13115     /**
13116      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13117      * anchor positions (defaults to 'tl-bl')
13118      */
13119     listAlign: 'tl-bl?',
13120     /**
13121      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13122      */
13123     maxHeight: 300,
13124     /**
13125      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13126      * query specified by the allQuery config option (defaults to 'query')
13127      */
13128     triggerAction: 'query',
13129     /**
13130      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13131      * (defaults to 4, does not apply if editable = false)
13132      */
13133     minChars : 4,
13134     /**
13135      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13136      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13137      */
13138     typeAhead: false,
13139     /**
13140      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13141      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13142      */
13143     queryDelay: 500,
13144     /**
13145      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13146      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13147      */
13148     pageSize: 0,
13149     /**
13150      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13151      * when editable = true (defaults to false)
13152      */
13153     selectOnFocus:false,
13154     /**
13155      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13156      */
13157     queryParam: 'query',
13158     /**
13159      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13160      * when mode = 'remote' (defaults to 'Loading...')
13161      */
13162     loadingText: 'Loading...',
13163     /**
13164      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13165      */
13166     resizable: false,
13167     /**
13168      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13169      */
13170     handleHeight : 8,
13171     /**
13172      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13173      * traditional select (defaults to true)
13174      */
13175     editable: true,
13176     /**
13177      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13178      */
13179     allQuery: '',
13180     /**
13181      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13182      */
13183     mode: 'remote',
13184     /**
13185      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13186      * listWidth has a higher value)
13187      */
13188     minListWidth : 70,
13189     /**
13190      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13191      * allow the user to set arbitrary text into the field (defaults to false)
13192      */
13193     forceSelection:false,
13194     /**
13195      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13196      * if typeAhead = true (defaults to 250)
13197      */
13198     typeAheadDelay : 250,
13199     /**
13200      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13201      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13202      */
13203     valueNotFoundText : undefined,
13204     /**
13205      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13206      */
13207     blockFocus : false,
13208     
13209     /**
13210      * @cfg {Boolean} disableClear Disable showing of clear button.
13211      */
13212     disableClear : false,
13213     /**
13214      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13215      */
13216     alwaysQuery : false,
13217     
13218     /**
13219      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13220      */
13221     multiple : false,
13222     
13223     /**
13224      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13225      */
13226     invalidClass : "has-warning",
13227     
13228     /**
13229      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13230      */
13231     validClass : "has-success",
13232     
13233     /**
13234      * @cfg {Boolean} specialFilter (true|false) special filter default false
13235      */
13236     specialFilter : false,
13237     
13238     /**
13239      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13240      */
13241     mobileTouchView : true,
13242     
13243     /**
13244      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13245      */
13246     useNativeIOS : false,
13247     
13248     /**
13249      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13250      */
13251     mobile_restrict_height : false,
13252     
13253     ios_options : false,
13254     
13255     //private
13256     addicon : false,
13257     editicon: false,
13258     
13259     page: 0,
13260     hasQuery: false,
13261     append: false,
13262     loadNext: false,
13263     autoFocus : true,
13264     tickable : false,
13265     btnPosition : 'right',
13266     triggerList : true,
13267     showToggleBtn : true,
13268     animate : true,
13269     emptyResultText: 'Empty',
13270     triggerText : 'Select',
13271     emptyTitle : '',
13272     
13273     // element that contains real text value.. (when hidden is used..)
13274     
13275     getAutoCreate : function()
13276     {   
13277         var cfg = false;
13278         //render
13279         /*
13280          * Render classic select for iso
13281          */
13282         
13283         if(Roo.isIOS && this.useNativeIOS){
13284             cfg = this.getAutoCreateNativeIOS();
13285             return cfg;
13286         }
13287         
13288         /*
13289          * Touch Devices
13290          */
13291         
13292         if(Roo.isTouch && this.mobileTouchView){
13293             cfg = this.getAutoCreateTouchView();
13294             return cfg;;
13295         }
13296         
13297         /*
13298          *  Normal ComboBox
13299          */
13300         if(!this.tickable){
13301             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13302             return cfg;
13303         }
13304         
13305         /*
13306          *  ComboBox with tickable selections
13307          */
13308              
13309         var align = this.labelAlign || this.parentLabelAlign();
13310         
13311         cfg = {
13312             cls : 'form-group roo-combobox-tickable' //input-group
13313         };
13314         
13315         var btn_text_select = '';
13316         var btn_text_done = '';
13317         var btn_text_cancel = '';
13318         
13319         if (this.btn_text_show) {
13320             btn_text_select = 'Select';
13321             btn_text_done = 'Done';
13322             btn_text_cancel = 'Cancel'; 
13323         }
13324         
13325         var buttons = {
13326             tag : 'div',
13327             cls : 'tickable-buttons',
13328             cn : [
13329                 {
13330                     tag : 'button',
13331                     type : 'button',
13332                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13333                     //html : this.triggerText
13334                     html: btn_text_select
13335                 },
13336                 {
13337                     tag : 'button',
13338                     type : 'button',
13339                     name : 'ok',
13340                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13341                     //html : 'Done'
13342                     html: btn_text_done
13343                 },
13344                 {
13345                     tag : 'button',
13346                     type : 'button',
13347                     name : 'cancel',
13348                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13349                     //html : 'Cancel'
13350                     html: btn_text_cancel
13351                 }
13352             ]
13353         };
13354         
13355         if(this.editable){
13356             buttons.cn.unshift({
13357                 tag: 'input',
13358                 cls: 'roo-select2-search-field-input'
13359             });
13360         }
13361         
13362         var _this = this;
13363         
13364         Roo.each(buttons.cn, function(c){
13365             if (_this.size) {
13366                 c.cls += ' btn-' + _this.size;
13367             }
13368
13369             if (_this.disabled) {
13370                 c.disabled = true;
13371             }
13372         });
13373         
13374         var box = {
13375             tag: 'div',
13376             style : 'display: contents',
13377             cn: [
13378                 {
13379                     tag: 'input',
13380                     type : 'hidden',
13381                     cls: 'form-hidden-field'
13382                 },
13383                 {
13384                     tag: 'ul',
13385                     cls: 'roo-select2-choices',
13386                     cn:[
13387                         {
13388                             tag: 'li',
13389                             cls: 'roo-select2-search-field',
13390                             cn: [
13391                                 buttons
13392                             ]
13393                         }
13394                     ]
13395                 }
13396             ]
13397         };
13398         
13399         var combobox = {
13400             cls: 'roo-select2-container input-group roo-select2-container-multi',
13401             cn: [
13402                 
13403                 box
13404 //                {
13405 //                    tag: 'ul',
13406 //                    cls: 'typeahead typeahead-long dropdown-menu',
13407 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13408 //                }
13409             ]
13410         };
13411         
13412         if(this.hasFeedback && !this.allowBlank){
13413             
13414             var feedback = {
13415                 tag: 'span',
13416                 cls: 'glyphicon form-control-feedback'
13417             };
13418
13419             combobox.cn.push(feedback);
13420         }
13421         
13422         var indicator = {
13423             tag : 'i',
13424             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13425             tooltip : 'This field is required'
13426         };
13427         if (Roo.bootstrap.version == 4) {
13428             indicator = {
13429                 tag : 'i',
13430                 style : 'display:none'
13431             };
13432         }
13433         if (align ==='left' && this.fieldLabel.length) {
13434             
13435             cfg.cls += ' roo-form-group-label-left row';
13436             
13437             cfg.cn = [
13438                 indicator,
13439                 {
13440                     tag: 'label',
13441                     'for' :  id,
13442                     cls : 'control-label col-form-label',
13443                     html : this.fieldLabel
13444
13445                 },
13446                 {
13447                     cls : "", 
13448                     cn: [
13449                         combobox
13450                     ]
13451                 }
13452
13453             ];
13454             
13455             var labelCfg = cfg.cn[1];
13456             var contentCfg = cfg.cn[2];
13457             
13458
13459             if(this.indicatorpos == 'right'){
13460                 
13461                 cfg.cn = [
13462                     {
13463                         tag: 'label',
13464                         'for' :  id,
13465                         cls : 'control-label col-form-label',
13466                         cn : [
13467                             {
13468                                 tag : 'span',
13469                                 html : this.fieldLabel
13470                             },
13471                             indicator
13472                         ]
13473                     },
13474                     {
13475                         cls : "",
13476                         cn: [
13477                             combobox
13478                         ]
13479                     }
13480
13481                 ];
13482                 
13483                 
13484                 
13485                 labelCfg = cfg.cn[0];
13486                 contentCfg = cfg.cn[1];
13487             
13488             }
13489             
13490             if(this.labelWidth > 12){
13491                 labelCfg.style = "width: " + this.labelWidth + 'px';
13492             }
13493             
13494             if(this.labelWidth < 13 && this.labelmd == 0){
13495                 this.labelmd = this.labelWidth;
13496             }
13497             
13498             if(this.labellg > 0){
13499                 labelCfg.cls += ' col-lg-' + this.labellg;
13500                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13501             }
13502             
13503             if(this.labelmd > 0){
13504                 labelCfg.cls += ' col-md-' + this.labelmd;
13505                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13506             }
13507             
13508             if(this.labelsm > 0){
13509                 labelCfg.cls += ' col-sm-' + this.labelsm;
13510                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13511             }
13512             
13513             if(this.labelxs > 0){
13514                 labelCfg.cls += ' col-xs-' + this.labelxs;
13515                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13516             }
13517                 
13518                 
13519         } else if ( this.fieldLabel.length) {
13520 //                Roo.log(" label");
13521                  cfg.cn = [
13522                    indicator,
13523                     {
13524                         tag: 'label',
13525                         //cls : 'input-group-addon',
13526                         html : this.fieldLabel
13527                     },
13528                     combobox
13529                 ];
13530                 
13531                 if(this.indicatorpos == 'right'){
13532                     cfg.cn = [
13533                         {
13534                             tag: 'label',
13535                             //cls : 'input-group-addon',
13536                             html : this.fieldLabel
13537                         },
13538                         indicator,
13539                         combobox
13540                     ];
13541                     
13542                 }
13543
13544         } else {
13545             
13546 //                Roo.log(" no label && no align");
13547                 cfg = combobox
13548                      
13549                 
13550         }
13551          
13552         var settings=this;
13553         ['xs','sm','md','lg'].map(function(size){
13554             if (settings[size]) {
13555                 cfg.cls += ' col-' + size + '-' + settings[size];
13556             }
13557         });
13558         
13559         return cfg;
13560         
13561     },
13562     
13563     _initEventsCalled : false,
13564     
13565     // private
13566     initEvents: function()
13567     {   
13568         if (this._initEventsCalled) { // as we call render... prevent looping...
13569             return;
13570         }
13571         this._initEventsCalled = true;
13572         
13573         if (!this.store) {
13574             throw "can not find store for combo";
13575         }
13576         
13577         this.indicator = this.indicatorEl();
13578         
13579         this.store = Roo.factory(this.store, Roo.data);
13580         this.store.parent = this;
13581         
13582         // if we are building from html. then this element is so complex, that we can not really
13583         // use the rendered HTML.
13584         // so we have to trash and replace the previous code.
13585         if (Roo.XComponent.build_from_html) {
13586             // remove this element....
13587             var e = this.el.dom, k=0;
13588             while (e ) { e = e.previousSibling;  ++k;}
13589
13590             this.el.remove();
13591             
13592             this.el=false;
13593             this.rendered = false;
13594             
13595             this.render(this.parent().getChildContainer(true), k);
13596         }
13597         
13598         if(Roo.isIOS && this.useNativeIOS){
13599             this.initIOSView();
13600             return;
13601         }
13602         
13603         /*
13604          * Touch Devices
13605          */
13606         
13607         if(Roo.isTouch && this.mobileTouchView){
13608             this.initTouchView();
13609             return;
13610         }
13611         
13612         if(this.tickable){
13613             this.initTickableEvents();
13614             return;
13615         }
13616         
13617         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13618         
13619         if(this.hiddenName){
13620             
13621             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13622             
13623             this.hiddenField.dom.value =
13624                 this.hiddenValue !== undefined ? this.hiddenValue :
13625                 this.value !== undefined ? this.value : '';
13626
13627             // prevent input submission
13628             this.el.dom.removeAttribute('name');
13629             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13630              
13631              
13632         }
13633         //if(Roo.isGecko){
13634         //    this.el.dom.setAttribute('autocomplete', 'off');
13635         //}
13636         
13637         var cls = 'x-combo-list';
13638         
13639         //this.list = new Roo.Layer({
13640         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13641         //});
13642         
13643         var _this = this;
13644         
13645         (function(){
13646             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13647             _this.list.setWidth(lw);
13648         }).defer(100);
13649         
13650         this.list.on('mouseover', this.onViewOver, this);
13651         this.list.on('mousemove', this.onViewMove, this);
13652         this.list.on('scroll', this.onViewScroll, this);
13653         
13654         /*
13655         this.list.swallowEvent('mousewheel');
13656         this.assetHeight = 0;
13657
13658         if(this.title){
13659             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13660             this.assetHeight += this.header.getHeight();
13661         }
13662
13663         this.innerList = this.list.createChild({cls:cls+'-inner'});
13664         this.innerList.on('mouseover', this.onViewOver, this);
13665         this.innerList.on('mousemove', this.onViewMove, this);
13666         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13667         
13668         if(this.allowBlank && !this.pageSize && !this.disableClear){
13669             this.footer = this.list.createChild({cls:cls+'-ft'});
13670             this.pageTb = new Roo.Toolbar(this.footer);
13671            
13672         }
13673         if(this.pageSize){
13674             this.footer = this.list.createChild({cls:cls+'-ft'});
13675             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13676                     {pageSize: this.pageSize});
13677             
13678         }
13679         
13680         if (this.pageTb && this.allowBlank && !this.disableClear) {
13681             var _this = this;
13682             this.pageTb.add(new Roo.Toolbar.Fill(), {
13683                 cls: 'x-btn-icon x-btn-clear',
13684                 text: '&#160;',
13685                 handler: function()
13686                 {
13687                     _this.collapse();
13688                     _this.clearValue();
13689                     _this.onSelect(false, -1);
13690                 }
13691             });
13692         }
13693         if (this.footer) {
13694             this.assetHeight += this.footer.getHeight();
13695         }
13696         */
13697             
13698         if(!this.tpl){
13699             this.tpl = Roo.bootstrap.version == 4 ?
13700                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13701                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13702         }
13703
13704         this.view = new Roo.View(this.list, this.tpl, {
13705             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13706         });
13707         //this.view.wrapEl.setDisplayed(false);
13708         this.view.on('click', this.onViewClick, this);
13709         
13710         
13711         this.store.on('beforeload', this.onBeforeLoad, this);
13712         this.store.on('load', this.onLoad, this);
13713         this.store.on('loadexception', this.onLoadException, this);
13714         /*
13715         if(this.resizable){
13716             this.resizer = new Roo.Resizable(this.list,  {
13717                pinned:true, handles:'se'
13718             });
13719             this.resizer.on('resize', function(r, w, h){
13720                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13721                 this.listWidth = w;
13722                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13723                 this.restrictHeight();
13724             }, this);
13725             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13726         }
13727         */
13728         if(!this.editable){
13729             this.editable = true;
13730             this.setEditable(false);
13731         }
13732         
13733         /*
13734         
13735         if (typeof(this.events.add.listeners) != 'undefined') {
13736             
13737             this.addicon = this.wrap.createChild(
13738                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13739        
13740             this.addicon.on('click', function(e) {
13741                 this.fireEvent('add', this);
13742             }, this);
13743         }
13744         if (typeof(this.events.edit.listeners) != 'undefined') {
13745             
13746             this.editicon = this.wrap.createChild(
13747                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13748             if (this.addicon) {
13749                 this.editicon.setStyle('margin-left', '40px');
13750             }
13751             this.editicon.on('click', function(e) {
13752                 
13753                 // we fire even  if inothing is selected..
13754                 this.fireEvent('edit', this, this.lastData );
13755                 
13756             }, this);
13757         }
13758         */
13759         
13760         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13761             "up" : function(e){
13762                 this.inKeyMode = true;
13763                 this.selectPrev();
13764             },
13765
13766             "down" : function(e){
13767                 if(!this.isExpanded()){
13768                     this.onTriggerClick();
13769                 }else{
13770                     this.inKeyMode = true;
13771                     this.selectNext();
13772                 }
13773             },
13774
13775             "enter" : function(e){
13776 //                this.onViewClick();
13777                 //return true;
13778                 this.collapse();
13779                 
13780                 if(this.fireEvent("specialkey", this, e)){
13781                     this.onViewClick(false);
13782                 }
13783                 
13784                 return true;
13785             },
13786
13787             "esc" : function(e){
13788                 this.collapse();
13789             },
13790
13791             "tab" : function(e){
13792                 this.collapse();
13793                 
13794                 if(this.fireEvent("specialkey", this, e)){
13795                     this.onViewClick(false);
13796                 }
13797                 
13798                 return true;
13799             },
13800
13801             scope : this,
13802
13803             doRelay : function(foo, bar, hname){
13804                 if(hname == 'down' || this.scope.isExpanded()){
13805                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13806                 }
13807                 return true;
13808             },
13809
13810             forceKeyDown: true
13811         });
13812         
13813         
13814         this.queryDelay = Math.max(this.queryDelay || 10,
13815                 this.mode == 'local' ? 10 : 250);
13816         
13817         
13818         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13819         
13820         if(this.typeAhead){
13821             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13822         }
13823         if(this.editable !== false){
13824             this.inputEl().on("keyup", this.onKeyUp, this);
13825         }
13826         if(this.forceSelection){
13827             this.inputEl().on('blur', this.doForce, this);
13828         }
13829         
13830         if(this.multiple){
13831             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13832             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13833         }
13834     },
13835     
13836     initTickableEvents: function()
13837     {   
13838         this.createList();
13839         
13840         if(this.hiddenName){
13841             
13842             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13843             
13844             this.hiddenField.dom.value =
13845                 this.hiddenValue !== undefined ? this.hiddenValue :
13846                 this.value !== undefined ? this.value : '';
13847
13848             // prevent input submission
13849             this.el.dom.removeAttribute('name');
13850             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13851              
13852              
13853         }
13854         
13855 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13856         
13857         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13858         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13859         if(this.triggerList){
13860             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13861         }
13862          
13863         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13864         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13865         
13866         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13867         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13868         
13869         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13870         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13871         
13872         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13873         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13874         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13875         
13876         this.okBtn.hide();
13877         this.cancelBtn.hide();
13878         
13879         var _this = this;
13880         
13881         (function(){
13882             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13883             _this.list.setWidth(lw);
13884         }).defer(100);
13885         
13886         this.list.on('mouseover', this.onViewOver, this);
13887         this.list.on('mousemove', this.onViewMove, this);
13888         
13889         this.list.on('scroll', this.onViewScroll, this);
13890         
13891         if(!this.tpl){
13892             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13893                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13894         }
13895
13896         this.view = new Roo.View(this.list, this.tpl, {
13897             singleSelect:true,
13898             tickable:true,
13899             parent:this,
13900             store: this.store,
13901             selectedClass: this.selectedClass
13902         });
13903         
13904         //this.view.wrapEl.setDisplayed(false);
13905         this.view.on('click', this.onViewClick, this);
13906         
13907         
13908         
13909         this.store.on('beforeload', this.onBeforeLoad, this);
13910         this.store.on('load', this.onLoad, this);
13911         this.store.on('loadexception', this.onLoadException, this);
13912         
13913         if(this.editable){
13914             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13915                 "up" : function(e){
13916                     this.inKeyMode = true;
13917                     this.selectPrev();
13918                 },
13919
13920                 "down" : function(e){
13921                     this.inKeyMode = true;
13922                     this.selectNext();
13923                 },
13924
13925                 "enter" : function(e){
13926                     if(this.fireEvent("specialkey", this, e)){
13927                         this.onViewClick(false);
13928                     }
13929                     
13930                     return true;
13931                 },
13932
13933                 "esc" : function(e){
13934                     this.onTickableFooterButtonClick(e, false, false);
13935                 },
13936
13937                 "tab" : function(e){
13938                     this.fireEvent("specialkey", this, e);
13939                     
13940                     this.onTickableFooterButtonClick(e, false, false);
13941                     
13942                     return true;
13943                 },
13944
13945                 scope : this,
13946
13947                 doRelay : function(e, fn, key){
13948                     if(this.scope.isExpanded()){
13949                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13950                     }
13951                     return true;
13952                 },
13953
13954                 forceKeyDown: true
13955             });
13956         }
13957         
13958         this.queryDelay = Math.max(this.queryDelay || 10,
13959                 this.mode == 'local' ? 10 : 250);
13960         
13961         
13962         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13963         
13964         if(this.typeAhead){
13965             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13966         }
13967         
13968         if(this.editable !== false){
13969             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13970         }
13971         
13972         this.indicator = this.indicatorEl();
13973         
13974         if(this.indicator){
13975             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13976             this.indicator.hide();
13977         }
13978         
13979     },
13980
13981     onDestroy : function(){
13982         if(this.view){
13983             this.view.setStore(null);
13984             this.view.el.removeAllListeners();
13985             this.view.el.remove();
13986             this.view.purgeListeners();
13987         }
13988         if(this.list){
13989             this.list.dom.innerHTML  = '';
13990         }
13991         
13992         if(this.store){
13993             this.store.un('beforeload', this.onBeforeLoad, this);
13994             this.store.un('load', this.onLoad, this);
13995             this.store.un('loadexception', this.onLoadException, this);
13996         }
13997         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13998     },
13999
14000     // private
14001     fireKey : function(e){
14002         if(e.isNavKeyPress() && !this.list.isVisible()){
14003             this.fireEvent("specialkey", this, e);
14004         }
14005     },
14006
14007     // private
14008     onResize: function(w, h){
14009 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14010 //        
14011 //        if(typeof w != 'number'){
14012 //            // we do not handle it!?!?
14013 //            return;
14014 //        }
14015 //        var tw = this.trigger.getWidth();
14016 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14017 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14018 //        var x = w - tw;
14019 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14020 //            
14021 //        //this.trigger.setStyle('left', x+'px');
14022 //        
14023 //        if(this.list && this.listWidth === undefined){
14024 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14025 //            this.list.setWidth(lw);
14026 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14027 //        }
14028         
14029     
14030         
14031     },
14032
14033     /**
14034      * Allow or prevent the user from directly editing the field text.  If false is passed,
14035      * the user will only be able to select from the items defined in the dropdown list.  This method
14036      * is the runtime equivalent of setting the 'editable' config option at config time.
14037      * @param {Boolean} value True to allow the user to directly edit the field text
14038      */
14039     setEditable : function(value){
14040         if(value == this.editable){
14041             return;
14042         }
14043         this.editable = value;
14044         if(!value){
14045             this.inputEl().dom.setAttribute('readOnly', true);
14046             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14047             this.inputEl().addClass('x-combo-noedit');
14048         }else{
14049             this.inputEl().dom.setAttribute('readOnly', false);
14050             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14051             this.inputEl().removeClass('x-combo-noedit');
14052         }
14053     },
14054
14055     // private
14056     
14057     onBeforeLoad : function(combo,opts){
14058         if(!this.hasFocus){
14059             return;
14060         }
14061          if (!opts.add) {
14062             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14063          }
14064         this.restrictHeight();
14065         this.selectedIndex = -1;
14066     },
14067
14068     // private
14069     onLoad : function(){
14070         
14071         this.hasQuery = false;
14072         
14073         if(!this.hasFocus){
14074             return;
14075         }
14076         
14077         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14078             this.loading.hide();
14079         }
14080         
14081         if(this.store.getCount() > 0){
14082             
14083             this.expand();
14084             this.restrictHeight();
14085             if(this.lastQuery == this.allQuery){
14086                 if(this.editable && !this.tickable){
14087                     this.inputEl().dom.select();
14088                 }
14089                 
14090                 if(
14091                     !this.selectByValue(this.value, true) &&
14092                     this.autoFocus && 
14093                     (
14094                         !this.store.lastOptions ||
14095                         typeof(this.store.lastOptions.add) == 'undefined' || 
14096                         this.store.lastOptions.add != true
14097                     )
14098                 ){
14099                     this.select(0, true);
14100                 }
14101             }else{
14102                 if(this.autoFocus){
14103                     this.selectNext();
14104                 }
14105                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14106                     this.taTask.delay(this.typeAheadDelay);
14107                 }
14108             }
14109         }else{
14110             this.onEmptyResults();
14111         }
14112         
14113         //this.el.focus();
14114     },
14115     // private
14116     onLoadException : function()
14117     {
14118         this.hasQuery = false;
14119         
14120         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14121             this.loading.hide();
14122         }
14123         
14124         if(this.tickable && this.editable){
14125             return;
14126         }
14127         
14128         this.collapse();
14129         // only causes errors at present
14130         //Roo.log(this.store.reader.jsonData);
14131         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14132             // fixme
14133             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14134         //}
14135         
14136         
14137     },
14138     // private
14139     onTypeAhead : function(){
14140         if(this.store.getCount() > 0){
14141             var r = this.store.getAt(0);
14142             var newValue = r.data[this.displayField];
14143             var len = newValue.length;
14144             var selStart = this.getRawValue().length;
14145             
14146             if(selStart != len){
14147                 this.setRawValue(newValue);
14148                 this.selectText(selStart, newValue.length);
14149             }
14150         }
14151     },
14152
14153     // private
14154     onSelect : function(record, index){
14155         
14156         if(this.fireEvent('beforeselect', this, record, index) !== false){
14157         
14158             this.setFromData(index > -1 ? record.data : false);
14159             
14160             this.collapse();
14161             this.fireEvent('select', this, record, index);
14162         }
14163     },
14164
14165     /**
14166      * Returns the currently selected field value or empty string if no value is set.
14167      * @return {String} value The selected value
14168      */
14169     getValue : function()
14170     {
14171         if(Roo.isIOS && this.useNativeIOS){
14172             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14173         }
14174         
14175         if(this.multiple){
14176             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14177         }
14178         
14179         if(this.valueField){
14180             return typeof this.value != 'undefined' ? this.value : '';
14181         }else{
14182             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14183         }
14184     },
14185     
14186     getRawValue : function()
14187     {
14188         if(Roo.isIOS && this.useNativeIOS){
14189             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14190         }
14191         
14192         var v = this.inputEl().getValue();
14193         
14194         return v;
14195     },
14196
14197     /**
14198      * Clears any text/value currently set in the field
14199      */
14200     clearValue : function(){
14201         
14202         if(this.hiddenField){
14203             this.hiddenField.dom.value = '';
14204         }
14205         this.value = '';
14206         this.setRawValue('');
14207         this.lastSelectionText = '';
14208         this.lastData = false;
14209         
14210         var close = this.closeTriggerEl();
14211         
14212         if(close){
14213             close.hide();
14214         }
14215         
14216         this.validate();
14217         
14218     },
14219
14220     /**
14221      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14222      * will be displayed in the field.  If the value does not match the data value of an existing item,
14223      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14224      * Otherwise the field will be blank (although the value will still be set).
14225      * @param {String} value The value to match
14226      */
14227     setValue : function(v)
14228     {
14229         if(Roo.isIOS && this.useNativeIOS){
14230             this.setIOSValue(v);
14231             return;
14232         }
14233         
14234         if(this.multiple){
14235             this.syncValue();
14236             return;
14237         }
14238         
14239         var text = v;
14240         if(this.valueField){
14241             var r = this.findRecord(this.valueField, v);
14242             if(r){
14243                 text = r.data[this.displayField];
14244             }else if(this.valueNotFoundText !== undefined){
14245                 text = this.valueNotFoundText;
14246             }
14247         }
14248         this.lastSelectionText = text;
14249         if(this.hiddenField){
14250             this.hiddenField.dom.value = v;
14251         }
14252         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14253         this.value = v;
14254         
14255         var close = this.closeTriggerEl();
14256         
14257         if(close){
14258             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14259         }
14260         
14261         this.validate();
14262     },
14263     /**
14264      * @property {Object} the last set data for the element
14265      */
14266     
14267     lastData : false,
14268     /**
14269      * Sets the value of the field based on a object which is related to the record format for the store.
14270      * @param {Object} value the value to set as. or false on reset?
14271      */
14272     setFromData : function(o){
14273         
14274         if(this.multiple){
14275             this.addItem(o);
14276             return;
14277         }
14278             
14279         var dv = ''; // display value
14280         var vv = ''; // value value..
14281         this.lastData = o;
14282         if (this.displayField) {
14283             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14284         } else {
14285             // this is an error condition!!!
14286             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14287         }
14288         
14289         if(this.valueField){
14290             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14291         }
14292         
14293         var close = this.closeTriggerEl();
14294         
14295         if(close){
14296             if(dv.length || vv * 1 > 0){
14297                 close.show() ;
14298                 this.blockFocus=true;
14299             } else {
14300                 close.hide();
14301             }             
14302         }
14303         
14304         if(this.hiddenField){
14305             this.hiddenField.dom.value = vv;
14306             
14307             this.lastSelectionText = dv;
14308             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14309             this.value = vv;
14310             return;
14311         }
14312         // no hidden field.. - we store the value in 'value', but still display
14313         // display field!!!!
14314         this.lastSelectionText = dv;
14315         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14316         this.value = vv;
14317         
14318         
14319         
14320     },
14321     // private
14322     reset : function(){
14323         // overridden so that last data is reset..
14324         
14325         if(this.multiple){
14326             this.clearItem();
14327             return;
14328         }
14329         
14330         this.setValue(this.originalValue);
14331         //this.clearInvalid();
14332         this.lastData = false;
14333         if (this.view) {
14334             this.view.clearSelections();
14335         }
14336         
14337         this.validate();
14338     },
14339     // private
14340     findRecord : function(prop, value){
14341         var record;
14342         if(this.store.getCount() > 0){
14343             this.store.each(function(r){
14344                 if(r.data[prop] == value){
14345                     record = r;
14346                     return false;
14347                 }
14348                 return true;
14349             });
14350         }
14351         return record;
14352     },
14353     
14354     getName: function()
14355     {
14356         // returns hidden if it's set..
14357         if (!this.rendered) {return ''};
14358         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14359         
14360     },
14361     // private
14362     onViewMove : function(e, t){
14363         this.inKeyMode = false;
14364     },
14365
14366     // private
14367     onViewOver : function(e, t){
14368         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14369             return;
14370         }
14371         var item = this.view.findItemFromChild(t);
14372         
14373         if(item){
14374             var index = this.view.indexOf(item);
14375             this.select(index, false);
14376         }
14377     },
14378
14379     // private
14380     onViewClick : function(view, doFocus, el, e)
14381     {
14382         var index = this.view.getSelectedIndexes()[0];
14383         
14384         var r = this.store.getAt(index);
14385         
14386         if(this.tickable){
14387             
14388             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14389                 return;
14390             }
14391             
14392             var rm = false;
14393             var _this = this;
14394             
14395             Roo.each(this.tickItems, function(v,k){
14396                 
14397                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14398                     Roo.log(v);
14399                     _this.tickItems.splice(k, 1);
14400                     
14401                     if(typeof(e) == 'undefined' && view == false){
14402                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14403                     }
14404                     
14405                     rm = true;
14406                     return;
14407                 }
14408             });
14409             
14410             if(rm){
14411                 return;
14412             }
14413             
14414             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14415                 this.tickItems.push(r.data);
14416             }
14417             
14418             if(typeof(e) == 'undefined' && view == false){
14419                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14420             }
14421                     
14422             return;
14423         }
14424         
14425         if(r){
14426             this.onSelect(r, index);
14427         }
14428         if(doFocus !== false && !this.blockFocus){
14429             this.inputEl().focus();
14430         }
14431     },
14432
14433     // private
14434     restrictHeight : function(){
14435         //this.innerList.dom.style.height = '';
14436         //var inner = this.innerList.dom;
14437         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14438         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14439         //this.list.beginUpdate();
14440         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14441         this.list.alignTo(this.inputEl(), this.listAlign);
14442         this.list.alignTo(this.inputEl(), this.listAlign);
14443         //this.list.endUpdate();
14444     },
14445
14446     // private
14447     onEmptyResults : function(){
14448         
14449         if(this.tickable && this.editable){
14450             this.hasFocus = false;
14451             this.restrictHeight();
14452             return;
14453         }
14454         
14455         this.collapse();
14456     },
14457
14458     /**
14459      * Returns true if the dropdown list is expanded, else false.
14460      */
14461     isExpanded : function(){
14462         return this.list.isVisible();
14463     },
14464
14465     /**
14466      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14467      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14468      * @param {String} value The data value of the item to select
14469      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14470      * selected item if it is not currently in view (defaults to true)
14471      * @return {Boolean} True if the value matched an item in the list, else false
14472      */
14473     selectByValue : function(v, scrollIntoView){
14474         if(v !== undefined && v !== null){
14475             var r = this.findRecord(this.valueField || this.displayField, v);
14476             if(r){
14477                 this.select(this.store.indexOf(r), scrollIntoView);
14478                 return true;
14479             }
14480         }
14481         return false;
14482     },
14483
14484     /**
14485      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14486      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14487      * @param {Number} index The zero-based index of the list item to select
14488      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14489      * selected item if it is not currently in view (defaults to true)
14490      */
14491     select : function(index, scrollIntoView){
14492         this.selectedIndex = index;
14493         this.view.select(index);
14494         if(scrollIntoView !== false){
14495             var el = this.view.getNode(index);
14496             /*
14497              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14498              */
14499             if(el){
14500                 this.list.scrollChildIntoView(el, false);
14501             }
14502         }
14503     },
14504
14505     // private
14506     selectNext : function(){
14507         var ct = this.store.getCount();
14508         if(ct > 0){
14509             if(this.selectedIndex == -1){
14510                 this.select(0);
14511             }else if(this.selectedIndex < ct-1){
14512                 this.select(this.selectedIndex+1);
14513             }
14514         }
14515     },
14516
14517     // private
14518     selectPrev : function(){
14519         var ct = this.store.getCount();
14520         if(ct > 0){
14521             if(this.selectedIndex == -1){
14522                 this.select(0);
14523             }else if(this.selectedIndex != 0){
14524                 this.select(this.selectedIndex-1);
14525             }
14526         }
14527     },
14528
14529     // private
14530     onKeyUp : function(e){
14531         if(this.editable !== false && !e.isSpecialKey()){
14532             this.lastKey = e.getKey();
14533             this.dqTask.delay(this.queryDelay);
14534         }
14535     },
14536
14537     // private
14538     validateBlur : function(){
14539         return !this.list || !this.list.isVisible();   
14540     },
14541
14542     // private
14543     initQuery : function(){
14544         
14545         var v = this.getRawValue();
14546         
14547         if(this.tickable && this.editable){
14548             v = this.tickableInputEl().getValue();
14549         }
14550         
14551         this.doQuery(v);
14552     },
14553
14554     // private
14555     doForce : function(){
14556         if(this.inputEl().dom.value.length > 0){
14557             this.inputEl().dom.value =
14558                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14559              
14560         }
14561     },
14562
14563     /**
14564      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14565      * query allowing the query action to be canceled if needed.
14566      * @param {String} query The SQL query to execute
14567      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14568      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14569      * saved in the current store (defaults to false)
14570      */
14571     doQuery : function(q, forceAll){
14572         
14573         if(q === undefined || q === null){
14574             q = '';
14575         }
14576         var qe = {
14577             query: q,
14578             forceAll: forceAll,
14579             combo: this,
14580             cancel:false
14581         };
14582         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14583             return false;
14584         }
14585         q = qe.query;
14586         
14587         forceAll = qe.forceAll;
14588         if(forceAll === true || (q.length >= this.minChars)){
14589             
14590             this.hasQuery = true;
14591             
14592             if(this.lastQuery != q || this.alwaysQuery){
14593                 this.lastQuery = q;
14594                 if(this.mode == 'local'){
14595                     this.selectedIndex = -1;
14596                     if(forceAll){
14597                         this.store.clearFilter();
14598                     }else{
14599                         
14600                         if(this.specialFilter){
14601                             this.fireEvent('specialfilter', this);
14602                             this.onLoad();
14603                             return;
14604                         }
14605                         
14606                         this.store.filter(this.displayField, q);
14607                     }
14608                     
14609                     this.store.fireEvent("datachanged", this.store);
14610                     
14611                     this.onLoad();
14612                     
14613                     
14614                 }else{
14615                     
14616                     this.store.baseParams[this.queryParam] = q;
14617                     
14618                     var options = {params : this.getParams(q)};
14619                     
14620                     if(this.loadNext){
14621                         options.add = true;
14622                         options.params.start = this.page * this.pageSize;
14623                     }
14624                     
14625                     this.store.load(options);
14626                     
14627                     /*
14628                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14629                      *  we should expand the list on onLoad
14630                      *  so command out it
14631                      */
14632 //                    this.expand();
14633                 }
14634             }else{
14635                 this.selectedIndex = -1;
14636                 this.onLoad();   
14637             }
14638         }
14639         
14640         this.loadNext = false;
14641     },
14642     
14643     // private
14644     getParams : function(q){
14645         var p = {};
14646         //p[this.queryParam] = q;
14647         
14648         if(this.pageSize){
14649             p.start = 0;
14650             p.limit = this.pageSize;
14651         }
14652         return p;
14653     },
14654
14655     /**
14656      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14657      */
14658     collapse : function(){
14659         if(!this.isExpanded()){
14660             return;
14661         }
14662         
14663         this.list.hide();
14664         
14665         this.hasFocus = false;
14666         
14667         if(this.tickable){
14668             this.okBtn.hide();
14669             this.cancelBtn.hide();
14670             this.trigger.show();
14671             
14672             if(this.editable){
14673                 this.tickableInputEl().dom.value = '';
14674                 this.tickableInputEl().blur();
14675             }
14676             
14677         }
14678         
14679         Roo.get(document).un('mousedown', this.collapseIf, this);
14680         Roo.get(document).un('mousewheel', this.collapseIf, this);
14681         if (!this.editable) {
14682             Roo.get(document).un('keydown', this.listKeyPress, this);
14683         }
14684         this.fireEvent('collapse', this);
14685         
14686         this.validate();
14687     },
14688
14689     // private
14690     collapseIf : function(e){
14691         var in_combo  = e.within(this.el);
14692         var in_list =  e.within(this.list);
14693         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14694         
14695         if (in_combo || in_list || is_list) {
14696             //e.stopPropagation();
14697             return;
14698         }
14699         
14700         if(this.tickable){
14701             this.onTickableFooterButtonClick(e, false, false);
14702         }
14703
14704         this.collapse();
14705         
14706     },
14707
14708     /**
14709      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14710      */
14711     expand : function(){
14712        
14713         if(this.isExpanded() || !this.hasFocus){
14714             return;
14715         }
14716         
14717         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14718         this.list.setWidth(lw);
14719         
14720         Roo.log('expand');
14721         
14722         this.list.show();
14723         
14724         this.restrictHeight();
14725         
14726         if(this.tickable){
14727             
14728             this.tickItems = Roo.apply([], this.item);
14729             
14730             this.okBtn.show();
14731             this.cancelBtn.show();
14732             this.trigger.hide();
14733             
14734             if(this.editable){
14735                 this.tickableInputEl().focus();
14736             }
14737             
14738         }
14739         
14740         Roo.get(document).on('mousedown', this.collapseIf, this);
14741         Roo.get(document).on('mousewheel', this.collapseIf, this);
14742         if (!this.editable) {
14743             Roo.get(document).on('keydown', this.listKeyPress, this);
14744         }
14745         
14746         this.fireEvent('expand', this);
14747     },
14748
14749     // private
14750     // Implements the default empty TriggerField.onTriggerClick function
14751     onTriggerClick : function(e)
14752     {
14753         Roo.log('trigger click');
14754         
14755         if(this.disabled || !this.triggerList){
14756             return;
14757         }
14758         
14759         this.page = 0;
14760         this.loadNext = false;
14761         
14762         if(this.isExpanded()){
14763             this.collapse();
14764             if (!this.blockFocus) {
14765                 this.inputEl().focus();
14766             }
14767             
14768         }else {
14769             this.hasFocus = true;
14770             if(this.triggerAction == 'all') {
14771                 this.doQuery(this.allQuery, true);
14772             } else {
14773                 this.doQuery(this.getRawValue());
14774             }
14775             if (!this.blockFocus) {
14776                 this.inputEl().focus();
14777             }
14778         }
14779     },
14780     
14781     onTickableTriggerClick : function(e)
14782     {
14783         if(this.disabled){
14784             return;
14785         }
14786         
14787         this.page = 0;
14788         this.loadNext = false;
14789         this.hasFocus = true;
14790         
14791         if(this.triggerAction == 'all') {
14792             this.doQuery(this.allQuery, true);
14793         } else {
14794             this.doQuery(this.getRawValue());
14795         }
14796     },
14797     
14798     onSearchFieldClick : function(e)
14799     {
14800         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14801             this.onTickableFooterButtonClick(e, false, false);
14802             return;
14803         }
14804         
14805         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14806             return;
14807         }
14808         
14809         this.page = 0;
14810         this.loadNext = false;
14811         this.hasFocus = true;
14812         
14813         if(this.triggerAction == 'all') {
14814             this.doQuery(this.allQuery, true);
14815         } else {
14816             this.doQuery(this.getRawValue());
14817         }
14818     },
14819     
14820     listKeyPress : function(e)
14821     {
14822         //Roo.log('listkeypress');
14823         // scroll to first matching element based on key pres..
14824         if (e.isSpecialKey()) {
14825             return false;
14826         }
14827         var k = String.fromCharCode(e.getKey()).toUpperCase();
14828         //Roo.log(k);
14829         var match  = false;
14830         var csel = this.view.getSelectedNodes();
14831         var cselitem = false;
14832         if (csel.length) {
14833             var ix = this.view.indexOf(csel[0]);
14834             cselitem  = this.store.getAt(ix);
14835             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14836                 cselitem = false;
14837             }
14838             
14839         }
14840         
14841         this.store.each(function(v) { 
14842             if (cselitem) {
14843                 // start at existing selection.
14844                 if (cselitem.id == v.id) {
14845                     cselitem = false;
14846                 }
14847                 return true;
14848             }
14849                 
14850             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14851                 match = this.store.indexOf(v);
14852                 return false;
14853             }
14854             return true;
14855         }, this);
14856         
14857         if (match === false) {
14858             return true; // no more action?
14859         }
14860         // scroll to?
14861         this.view.select(match);
14862         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14863         sn.scrollIntoView(sn.dom.parentNode, false);
14864     },
14865     
14866     onViewScroll : function(e, t){
14867         
14868         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
14869             return;
14870         }
14871         
14872         this.hasQuery = true;
14873         
14874         this.loading = this.list.select('.loading', true).first();
14875         
14876         if(this.loading === null){
14877             this.list.createChild({
14878                 tag: 'div',
14879                 cls: 'loading roo-select2-more-results roo-select2-active',
14880                 html: 'Loading more results...'
14881             });
14882             
14883             this.loading = this.list.select('.loading', true).first();
14884             
14885             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14886             
14887             this.loading.hide();
14888         }
14889         
14890         this.loading.show();
14891         
14892         var _combo = this;
14893         
14894         this.page++;
14895         this.loadNext = true;
14896         
14897         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14898         
14899         return;
14900     },
14901     
14902     addItem : function(o)
14903     {   
14904         var dv = ''; // display value
14905         
14906         if (this.displayField) {
14907             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14908         } else {
14909             // this is an error condition!!!
14910             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14911         }
14912         
14913         if(!dv.length){
14914             return;
14915         }
14916         
14917         var choice = this.choices.createChild({
14918             tag: 'li',
14919             cls: 'roo-select2-search-choice',
14920             cn: [
14921                 {
14922                     tag: 'div',
14923                     html: dv
14924                 },
14925                 {
14926                     tag: 'a',
14927                     href: '#',
14928                     cls: 'roo-select2-search-choice-close fa fa-times',
14929                     tabindex: '-1'
14930                 }
14931             ]
14932             
14933         }, this.searchField);
14934         
14935         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14936         
14937         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14938         
14939         this.item.push(o);
14940         
14941         this.lastData = o;
14942         
14943         this.syncValue();
14944         
14945         this.inputEl().dom.value = '';
14946         
14947         this.validate();
14948     },
14949     
14950     onRemoveItem : function(e, _self, o)
14951     {
14952         e.preventDefault();
14953         
14954         this.lastItem = Roo.apply([], this.item);
14955         
14956         var index = this.item.indexOf(o.data) * 1;
14957         
14958         if( index < 0){
14959             Roo.log('not this item?!');
14960             return;
14961         }
14962         
14963         this.item.splice(index, 1);
14964         o.item.remove();
14965         
14966         this.syncValue();
14967         
14968         this.fireEvent('remove', this, e);
14969         
14970         this.validate();
14971         
14972     },
14973     
14974     syncValue : function()
14975     {
14976         if(!this.item.length){
14977             this.clearValue();
14978             return;
14979         }
14980             
14981         var value = [];
14982         var _this = this;
14983         Roo.each(this.item, function(i){
14984             if(_this.valueField){
14985                 value.push(i[_this.valueField]);
14986                 return;
14987             }
14988
14989             value.push(i);
14990         });
14991
14992         this.value = value.join(',');
14993
14994         if(this.hiddenField){
14995             this.hiddenField.dom.value = this.value;
14996         }
14997         
14998         this.store.fireEvent("datachanged", this.store);
14999         
15000         this.validate();
15001     },
15002     
15003     clearItem : function()
15004     {
15005         if(!this.multiple){
15006             return;
15007         }
15008         
15009         this.item = [];
15010         
15011         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15012            c.remove();
15013         });
15014         
15015         this.syncValue();
15016         
15017         this.validate();
15018         
15019         if(this.tickable && !Roo.isTouch){
15020             this.view.refresh();
15021         }
15022     },
15023     
15024     inputEl: function ()
15025     {
15026         if(Roo.isIOS && this.useNativeIOS){
15027             return this.el.select('select.roo-ios-select', true).first();
15028         }
15029         
15030         if(Roo.isTouch && this.mobileTouchView){
15031             return this.el.select('input.form-control',true).first();
15032         }
15033         
15034         if(this.tickable){
15035             return this.searchField;
15036         }
15037         
15038         return this.el.select('input.form-control',true).first();
15039     },
15040     
15041     onTickableFooterButtonClick : function(e, btn, el)
15042     {
15043         e.preventDefault();
15044         
15045         this.lastItem = Roo.apply([], this.item);
15046         
15047         if(btn && btn.name == 'cancel'){
15048             this.tickItems = Roo.apply([], this.item);
15049             this.collapse();
15050             return;
15051         }
15052         
15053         this.clearItem();
15054         
15055         var _this = this;
15056         
15057         Roo.each(this.tickItems, function(o){
15058             _this.addItem(o);
15059         });
15060         
15061         this.collapse();
15062         
15063     },
15064     
15065     validate : function()
15066     {
15067         if(this.getVisibilityEl().hasClass('hidden')){
15068             return true;
15069         }
15070         
15071         var v = this.getRawValue();
15072         
15073         if(this.multiple){
15074             v = this.getValue();
15075         }
15076         
15077         if(this.disabled || this.allowBlank || v.length){
15078             this.markValid();
15079             return true;
15080         }
15081         
15082         this.markInvalid();
15083         return false;
15084     },
15085     
15086     tickableInputEl : function()
15087     {
15088         if(!this.tickable || !this.editable){
15089             return this.inputEl();
15090         }
15091         
15092         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15093     },
15094     
15095     
15096     getAutoCreateTouchView : function()
15097     {
15098         var id = Roo.id();
15099         
15100         var cfg = {
15101             cls: 'form-group' //input-group
15102         };
15103         
15104         var input =  {
15105             tag: 'input',
15106             id : id,
15107             type : this.inputType,
15108             cls : 'form-control x-combo-noedit',
15109             autocomplete: 'new-password',
15110             placeholder : this.placeholder || '',
15111             readonly : true
15112         };
15113         
15114         if (this.name) {
15115             input.name = this.name;
15116         }
15117         
15118         if (this.size) {
15119             input.cls += ' input-' + this.size;
15120         }
15121         
15122         if (this.disabled) {
15123             input.disabled = true;
15124         }
15125         
15126         var inputblock = {
15127             cls : '',
15128             cn : [
15129                 input
15130             ]
15131         };
15132         
15133         if(this.before){
15134             inputblock.cls += ' input-group';
15135             
15136             inputblock.cn.unshift({
15137                 tag :'span',
15138                 cls : 'input-group-addon input-group-prepend input-group-text',
15139                 html : this.before
15140             });
15141         }
15142         
15143         if(this.removable && !this.multiple){
15144             inputblock.cls += ' roo-removable';
15145             
15146             inputblock.cn.push({
15147                 tag: 'button',
15148                 html : 'x',
15149                 cls : 'roo-combo-removable-btn close'
15150             });
15151         }
15152
15153         if(this.hasFeedback && !this.allowBlank){
15154             
15155             inputblock.cls += ' has-feedback';
15156             
15157             inputblock.cn.push({
15158                 tag: 'span',
15159                 cls: 'glyphicon form-control-feedback'
15160             });
15161             
15162         }
15163         
15164         if (this.after) {
15165             
15166             inputblock.cls += (this.before) ? '' : ' input-group';
15167             
15168             inputblock.cn.push({
15169                 tag :'span',
15170                 cls : 'input-group-addon input-group-append input-group-text',
15171                 html : this.after
15172             });
15173         }
15174
15175         
15176         var ibwrap = inputblock;
15177         
15178         if(this.multiple){
15179             ibwrap = {
15180                 tag: 'ul',
15181                 cls: 'roo-select2-choices',
15182                 cn:[
15183                     {
15184                         tag: 'li',
15185                         cls: 'roo-select2-search-field',
15186                         cn: [
15187
15188                             inputblock
15189                         ]
15190                     }
15191                 ]
15192             };
15193         
15194             
15195         }
15196         
15197         var combobox = {
15198             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15199             cn: [
15200                 {
15201                     tag: 'input',
15202                     type : 'hidden',
15203                     cls: 'form-hidden-field'
15204                 },
15205                 ibwrap
15206             ]
15207         };
15208         
15209         if(!this.multiple && this.showToggleBtn){
15210             
15211             var caret = {
15212                         tag: 'span',
15213                         cls: 'caret'
15214             };
15215             
15216             if (this.caret != false) {
15217                 caret = {
15218                      tag: 'i',
15219                      cls: 'fa fa-' + this.caret
15220                 };
15221                 
15222             }
15223             
15224             combobox.cn.push({
15225                 tag :'span',
15226                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15227                 cn : [
15228                     caret,
15229                     {
15230                         tag: 'span',
15231                         cls: 'combobox-clear',
15232                         cn  : [
15233                             {
15234                                 tag : 'i',
15235                                 cls: 'icon-remove'
15236                             }
15237                         ]
15238                     }
15239                 ]
15240
15241             })
15242         }
15243         
15244         if(this.multiple){
15245             combobox.cls += ' roo-select2-container-multi';
15246         }
15247         
15248         var align = this.labelAlign || this.parentLabelAlign();
15249         
15250         if (align ==='left' && this.fieldLabel.length) {
15251
15252             cfg.cn = [
15253                 {
15254                    tag : 'i',
15255                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15256                    tooltip : 'This field is required'
15257                 },
15258                 {
15259                     tag: 'label',
15260                     cls : 'control-label col-form-label',
15261                     html : this.fieldLabel
15262
15263                 },
15264                 {
15265                     cls : '', 
15266                     cn: [
15267                         combobox
15268                     ]
15269                 }
15270             ];
15271             
15272             var labelCfg = cfg.cn[1];
15273             var contentCfg = cfg.cn[2];
15274             
15275
15276             if(this.indicatorpos == 'right'){
15277                 cfg.cn = [
15278                     {
15279                         tag: 'label',
15280                         'for' :  id,
15281                         cls : 'control-label col-form-label',
15282                         cn : [
15283                             {
15284                                 tag : 'span',
15285                                 html : this.fieldLabel
15286                             },
15287                             {
15288                                 tag : 'i',
15289                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15290                                 tooltip : 'This field is required'
15291                             }
15292                         ]
15293                     },
15294                     {
15295                         cls : "",
15296                         cn: [
15297                             combobox
15298                         ]
15299                     }
15300
15301                 ];
15302                 
15303                 labelCfg = cfg.cn[0];
15304                 contentCfg = cfg.cn[1];
15305             }
15306             
15307            
15308             
15309             if(this.labelWidth > 12){
15310                 labelCfg.style = "width: " + this.labelWidth + 'px';
15311             }
15312             
15313             if(this.labelWidth < 13 && this.labelmd == 0){
15314                 this.labelmd = this.labelWidth;
15315             }
15316             
15317             if(this.labellg > 0){
15318                 labelCfg.cls += ' col-lg-' + this.labellg;
15319                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15320             }
15321             
15322             if(this.labelmd > 0){
15323                 labelCfg.cls += ' col-md-' + this.labelmd;
15324                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15325             }
15326             
15327             if(this.labelsm > 0){
15328                 labelCfg.cls += ' col-sm-' + this.labelsm;
15329                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15330             }
15331             
15332             if(this.labelxs > 0){
15333                 labelCfg.cls += ' col-xs-' + this.labelxs;
15334                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15335             }
15336                 
15337                 
15338         } else if ( this.fieldLabel.length) {
15339             cfg.cn = [
15340                 {
15341                    tag : 'i',
15342                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15343                    tooltip : 'This field is required'
15344                 },
15345                 {
15346                     tag: 'label',
15347                     cls : 'control-label',
15348                     html : this.fieldLabel
15349
15350                 },
15351                 {
15352                     cls : '', 
15353                     cn: [
15354                         combobox
15355                     ]
15356                 }
15357             ];
15358             
15359             if(this.indicatorpos == 'right'){
15360                 cfg.cn = [
15361                     {
15362                         tag: 'label',
15363                         cls : 'control-label',
15364                         html : this.fieldLabel,
15365                         cn : [
15366                             {
15367                                tag : 'i',
15368                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15369                                tooltip : 'This field is required'
15370                             }
15371                         ]
15372                     },
15373                     {
15374                         cls : '', 
15375                         cn: [
15376                             combobox
15377                         ]
15378                     }
15379                 ];
15380             }
15381         } else {
15382             cfg.cn = combobox;    
15383         }
15384         
15385         
15386         var settings = this;
15387         
15388         ['xs','sm','md','lg'].map(function(size){
15389             if (settings[size]) {
15390                 cfg.cls += ' col-' + size + '-' + settings[size];
15391             }
15392         });
15393         
15394         return cfg;
15395     },
15396     
15397     initTouchView : function()
15398     {
15399         this.renderTouchView();
15400         
15401         this.touchViewEl.on('scroll', function(){
15402             this.el.dom.scrollTop = 0;
15403         }, this);
15404         
15405         this.originalValue = this.getValue();
15406         
15407         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15408         
15409         this.inputEl().on("click", this.showTouchView, this);
15410         if (this.triggerEl) {
15411             this.triggerEl.on("click", this.showTouchView, this);
15412         }
15413         
15414         
15415         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15416         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15417         
15418         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15419         
15420         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15421         this.store.on('load', this.onTouchViewLoad, this);
15422         this.store.on('loadexception', this.onTouchViewLoadException, this);
15423         
15424         if(this.hiddenName){
15425             
15426             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15427             
15428             this.hiddenField.dom.value =
15429                 this.hiddenValue !== undefined ? this.hiddenValue :
15430                 this.value !== undefined ? this.value : '';
15431         
15432             this.el.dom.removeAttribute('name');
15433             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15434         }
15435         
15436         if(this.multiple){
15437             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15438             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15439         }
15440         
15441         if(this.removable && !this.multiple){
15442             var close = this.closeTriggerEl();
15443             if(close){
15444                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15445                 close.on('click', this.removeBtnClick, this, close);
15446             }
15447         }
15448         /*
15449          * fix the bug in Safari iOS8
15450          */
15451         this.inputEl().on("focus", function(e){
15452             document.activeElement.blur();
15453         }, this);
15454         
15455         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15456         
15457         return;
15458         
15459         
15460     },
15461     
15462     renderTouchView : function()
15463     {
15464         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15465         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15466         
15467         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15468         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15469         
15470         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15471         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15472         this.touchViewBodyEl.setStyle('overflow', 'auto');
15473         
15474         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15475         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15476         
15477         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15478         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15479         
15480     },
15481     
15482     showTouchView : function()
15483     {
15484         if(this.disabled){
15485             return;
15486         }
15487         
15488         this.touchViewHeaderEl.hide();
15489
15490         if(this.modalTitle.length){
15491             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15492             this.touchViewHeaderEl.show();
15493         }
15494
15495         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15496         this.touchViewEl.show();
15497
15498         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15499         
15500         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15501         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15502
15503         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15504
15505         if(this.modalTitle.length){
15506             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15507         }
15508         
15509         this.touchViewBodyEl.setHeight(bodyHeight);
15510
15511         if(this.animate){
15512             var _this = this;
15513             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15514         }else{
15515             this.touchViewEl.addClass('in');
15516         }
15517         
15518         if(this._touchViewMask){
15519             Roo.get(document.body).addClass("x-body-masked");
15520             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15521             this._touchViewMask.setStyle('z-index', 10000);
15522             this._touchViewMask.addClass('show');
15523         }
15524         
15525         this.doTouchViewQuery();
15526         
15527     },
15528     
15529     hideTouchView : function()
15530     {
15531         this.touchViewEl.removeClass('in');
15532
15533         if(this.animate){
15534             var _this = this;
15535             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15536         }else{
15537             this.touchViewEl.setStyle('display', 'none');
15538         }
15539         
15540         if(this._touchViewMask){
15541             this._touchViewMask.removeClass('show');
15542             Roo.get(document.body).removeClass("x-body-masked");
15543         }
15544     },
15545     
15546     setTouchViewValue : function()
15547     {
15548         if(this.multiple){
15549             this.clearItem();
15550         
15551             var _this = this;
15552
15553             Roo.each(this.tickItems, function(o){
15554                 this.addItem(o);
15555             }, this);
15556         }
15557         
15558         this.hideTouchView();
15559     },
15560     
15561     doTouchViewQuery : function()
15562     {
15563         var qe = {
15564             query: '',
15565             forceAll: true,
15566             combo: this,
15567             cancel:false
15568         };
15569         
15570         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15571             return false;
15572         }
15573         
15574         if(!this.alwaysQuery || this.mode == 'local'){
15575             this.onTouchViewLoad();
15576             return;
15577         }
15578         
15579         this.store.load();
15580     },
15581     
15582     onTouchViewBeforeLoad : function(combo,opts)
15583     {
15584         return;
15585     },
15586
15587     // private
15588     onTouchViewLoad : function()
15589     {
15590         if(this.store.getCount() < 1){
15591             this.onTouchViewEmptyResults();
15592             return;
15593         }
15594         
15595         this.clearTouchView();
15596         
15597         var rawValue = this.getRawValue();
15598         
15599         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15600         
15601         this.tickItems = [];
15602         
15603         this.store.data.each(function(d, rowIndex){
15604             var row = this.touchViewListGroup.createChild(template);
15605             
15606             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15607                 row.addClass(d.data.cls);
15608             }
15609             
15610             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15611                 var cfg = {
15612                     data : d.data,
15613                     html : d.data[this.displayField]
15614                 };
15615                 
15616                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15617                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15618                 }
15619             }
15620             row.removeClass('selected');
15621             if(!this.multiple && this.valueField &&
15622                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15623             {
15624                 // radio buttons..
15625                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15626                 row.addClass('selected');
15627             }
15628             
15629             if(this.multiple && this.valueField &&
15630                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15631             {
15632                 
15633                 // checkboxes...
15634                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15635                 this.tickItems.push(d.data);
15636             }
15637             
15638             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15639             
15640         }, this);
15641         
15642         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15643         
15644         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15645
15646         if(this.modalTitle.length){
15647             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15648         }
15649
15650         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15651         
15652         if(this.mobile_restrict_height && listHeight < bodyHeight){
15653             this.touchViewBodyEl.setHeight(listHeight);
15654         }
15655         
15656         var _this = this;
15657         
15658         if(firstChecked && listHeight > bodyHeight){
15659             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15660         }
15661         
15662     },
15663     
15664     onTouchViewLoadException : function()
15665     {
15666         this.hideTouchView();
15667     },
15668     
15669     onTouchViewEmptyResults : function()
15670     {
15671         this.clearTouchView();
15672         
15673         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15674         
15675         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15676         
15677     },
15678     
15679     clearTouchView : function()
15680     {
15681         this.touchViewListGroup.dom.innerHTML = '';
15682     },
15683     
15684     onTouchViewClick : function(e, el, o)
15685     {
15686         e.preventDefault();
15687         
15688         var row = o.row;
15689         var rowIndex = o.rowIndex;
15690         
15691         var r = this.store.getAt(rowIndex);
15692         
15693         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15694             
15695             if(!this.multiple){
15696                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15697                     c.dom.removeAttribute('checked');
15698                 }, this);
15699
15700                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15701
15702                 this.setFromData(r.data);
15703
15704                 var close = this.closeTriggerEl();
15705
15706                 if(close){
15707                     close.show();
15708                 }
15709
15710                 this.hideTouchView();
15711
15712                 this.fireEvent('select', this, r, rowIndex);
15713
15714                 return;
15715             }
15716
15717             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15718                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15719                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15720                 return;
15721             }
15722
15723             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15724             this.addItem(r.data);
15725             this.tickItems.push(r.data);
15726         }
15727     },
15728     
15729     getAutoCreateNativeIOS : function()
15730     {
15731         var cfg = {
15732             cls: 'form-group' //input-group,
15733         };
15734         
15735         var combobox =  {
15736             tag: 'select',
15737             cls : 'roo-ios-select'
15738         };
15739         
15740         if (this.name) {
15741             combobox.name = this.name;
15742         }
15743         
15744         if (this.disabled) {
15745             combobox.disabled = true;
15746         }
15747         
15748         var settings = this;
15749         
15750         ['xs','sm','md','lg'].map(function(size){
15751             if (settings[size]) {
15752                 cfg.cls += ' col-' + size + '-' + settings[size];
15753             }
15754         });
15755         
15756         cfg.cn = combobox;
15757         
15758         return cfg;
15759         
15760     },
15761     
15762     initIOSView : function()
15763     {
15764         this.store.on('load', this.onIOSViewLoad, this);
15765         
15766         return;
15767     },
15768     
15769     onIOSViewLoad : function()
15770     {
15771         if(this.store.getCount() < 1){
15772             return;
15773         }
15774         
15775         this.clearIOSView();
15776         
15777         if(this.allowBlank) {
15778             
15779             var default_text = '-- SELECT --';
15780             
15781             if(this.placeholder.length){
15782                 default_text = this.placeholder;
15783             }
15784             
15785             if(this.emptyTitle.length){
15786                 default_text += ' - ' + this.emptyTitle + ' -';
15787             }
15788             
15789             var opt = this.inputEl().createChild({
15790                 tag: 'option',
15791                 value : 0,
15792                 html : default_text
15793             });
15794             
15795             var o = {};
15796             o[this.valueField] = 0;
15797             o[this.displayField] = default_text;
15798             
15799             this.ios_options.push({
15800                 data : o,
15801                 el : opt
15802             });
15803             
15804         }
15805         
15806         this.store.data.each(function(d, rowIndex){
15807             
15808             var html = '';
15809             
15810             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15811                 html = d.data[this.displayField];
15812             }
15813             
15814             var value = '';
15815             
15816             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15817                 value = d.data[this.valueField];
15818             }
15819             
15820             var option = {
15821                 tag: 'option',
15822                 value : value,
15823                 html : html
15824             };
15825             
15826             if(this.value == d.data[this.valueField]){
15827                 option['selected'] = true;
15828             }
15829             
15830             var opt = this.inputEl().createChild(option);
15831             
15832             this.ios_options.push({
15833                 data : d.data,
15834                 el : opt
15835             });
15836             
15837         }, this);
15838         
15839         this.inputEl().on('change', function(){
15840            this.fireEvent('select', this);
15841         }, this);
15842         
15843     },
15844     
15845     clearIOSView: function()
15846     {
15847         this.inputEl().dom.innerHTML = '';
15848         
15849         this.ios_options = [];
15850     },
15851     
15852     setIOSValue: function(v)
15853     {
15854         this.value = v;
15855         
15856         if(!this.ios_options){
15857             return;
15858         }
15859         
15860         Roo.each(this.ios_options, function(opts){
15861            
15862            opts.el.dom.removeAttribute('selected');
15863            
15864            if(opts.data[this.valueField] != v){
15865                return;
15866            }
15867            
15868            opts.el.dom.setAttribute('selected', true);
15869            
15870         }, this);
15871     }
15872
15873     /** 
15874     * @cfg {Boolean} grow 
15875     * @hide 
15876     */
15877     /** 
15878     * @cfg {Number} growMin 
15879     * @hide 
15880     */
15881     /** 
15882     * @cfg {Number} growMax 
15883     * @hide 
15884     */
15885     /**
15886      * @hide
15887      * @method autoSize
15888      */
15889 });
15890
15891 Roo.apply(Roo.bootstrap.ComboBox,  {
15892     
15893     header : {
15894         tag: 'div',
15895         cls: 'modal-header',
15896         cn: [
15897             {
15898                 tag: 'h4',
15899                 cls: 'modal-title'
15900             }
15901         ]
15902     },
15903     
15904     body : {
15905         tag: 'div',
15906         cls: 'modal-body',
15907         cn: [
15908             {
15909                 tag: 'ul',
15910                 cls: 'list-group'
15911             }
15912         ]
15913     },
15914     
15915     listItemRadio : {
15916         tag: 'li',
15917         cls: 'list-group-item',
15918         cn: [
15919             {
15920                 tag: 'span',
15921                 cls: 'roo-combobox-list-group-item-value'
15922             },
15923             {
15924                 tag: 'div',
15925                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15926                 cn: [
15927                     {
15928                         tag: 'input',
15929                         type: 'radio'
15930                     },
15931                     {
15932                         tag: 'label'
15933                     }
15934                 ]
15935             }
15936         ]
15937     },
15938     
15939     listItemCheckbox : {
15940         tag: 'li',
15941         cls: 'list-group-item',
15942         cn: [
15943             {
15944                 tag: 'span',
15945                 cls: 'roo-combobox-list-group-item-value'
15946             },
15947             {
15948                 tag: 'div',
15949                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15950                 cn: [
15951                     {
15952                         tag: 'input',
15953                         type: 'checkbox'
15954                     },
15955                     {
15956                         tag: 'label'
15957                     }
15958                 ]
15959             }
15960         ]
15961     },
15962     
15963     emptyResult : {
15964         tag: 'div',
15965         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15966     },
15967     
15968     footer : {
15969         tag: 'div',
15970         cls: 'modal-footer',
15971         cn: [
15972             {
15973                 tag: 'div',
15974                 cls: 'row',
15975                 cn: [
15976                     {
15977                         tag: 'div',
15978                         cls: 'col-xs-6 text-left',
15979                         cn: {
15980                             tag: 'button',
15981                             cls: 'btn btn-danger roo-touch-view-cancel',
15982                             html: 'Cancel'
15983                         }
15984                     },
15985                     {
15986                         tag: 'div',
15987                         cls: 'col-xs-6 text-right',
15988                         cn: {
15989                             tag: 'button',
15990                             cls: 'btn btn-success roo-touch-view-ok',
15991                             html: 'OK'
15992                         }
15993                     }
15994                 ]
15995             }
15996         ]
15997         
15998     }
15999 });
16000
16001 Roo.apply(Roo.bootstrap.ComboBox,  {
16002     
16003     touchViewTemplate : {
16004         tag: 'div',
16005         cls: 'modal fade roo-combobox-touch-view',
16006         cn: [
16007             {
16008                 tag: 'div',
16009                 cls: 'modal-dialog',
16010                 style : 'position:fixed', // we have to fix position....
16011                 cn: [
16012                     {
16013                         tag: 'div',
16014                         cls: 'modal-content',
16015                         cn: [
16016                             Roo.bootstrap.ComboBox.header,
16017                             Roo.bootstrap.ComboBox.body,
16018                             Roo.bootstrap.ComboBox.footer
16019                         ]
16020                     }
16021                 ]
16022             }
16023         ]
16024     }
16025 });/*
16026  * Based on:
16027  * Ext JS Library 1.1.1
16028  * Copyright(c) 2006-2007, Ext JS, LLC.
16029  *
16030  * Originally Released Under LGPL - original licence link has changed is not relivant.
16031  *
16032  * Fork - LGPL
16033  * <script type="text/javascript">
16034  */
16035
16036 /**
16037  * @class Roo.View
16038  * @extends Roo.util.Observable
16039  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16040  * This class also supports single and multi selection modes. <br>
16041  * Create a data model bound view:
16042  <pre><code>
16043  var store = new Roo.data.Store(...);
16044
16045  var view = new Roo.View({
16046     el : "my-element",
16047     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16048  
16049     singleSelect: true,
16050     selectedClass: "ydataview-selected",
16051     store: store
16052  });
16053
16054  // listen for node click?
16055  view.on("click", function(vw, index, node, e){
16056  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16057  });
16058
16059  // load XML data
16060  dataModel.load("foobar.xml");
16061  </code></pre>
16062  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16063  * <br><br>
16064  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16065  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16066  * 
16067  * Note: old style constructor is still suported (container, template, config)
16068  * 
16069  * @constructor
16070  * Create a new View
16071  * @param {Object} config The config object
16072  * 
16073  */
16074 Roo.View = function(config, depreciated_tpl, depreciated_config){
16075     
16076     this.parent = false;
16077     
16078     if (typeof(depreciated_tpl) == 'undefined') {
16079         // new way.. - universal constructor.
16080         Roo.apply(this, config);
16081         this.el  = Roo.get(this.el);
16082     } else {
16083         // old format..
16084         this.el  = Roo.get(config);
16085         this.tpl = depreciated_tpl;
16086         Roo.apply(this, depreciated_config);
16087     }
16088     this.wrapEl  = this.el.wrap().wrap();
16089     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16090     
16091     
16092     if(typeof(this.tpl) == "string"){
16093         this.tpl = new Roo.Template(this.tpl);
16094     } else {
16095         // support xtype ctors..
16096         this.tpl = new Roo.factory(this.tpl, Roo);
16097     }
16098     
16099     
16100     this.tpl.compile();
16101     
16102     /** @private */
16103     this.addEvents({
16104         /**
16105          * @event beforeclick
16106          * Fires before a click is processed. Returns false to cancel the default action.
16107          * @param {Roo.View} this
16108          * @param {Number} index The index of the target node
16109          * @param {HTMLElement} node The target node
16110          * @param {Roo.EventObject} e The raw event object
16111          */
16112             "beforeclick" : true,
16113         /**
16114          * @event click
16115          * Fires when a template node is clicked.
16116          * @param {Roo.View} this
16117          * @param {Number} index The index of the target node
16118          * @param {HTMLElement} node The target node
16119          * @param {Roo.EventObject} e The raw event object
16120          */
16121             "click" : true,
16122         /**
16123          * @event dblclick
16124          * Fires when a template node is double clicked.
16125          * @param {Roo.View} this
16126          * @param {Number} index The index of the target node
16127          * @param {HTMLElement} node The target node
16128          * @param {Roo.EventObject} e The raw event object
16129          */
16130             "dblclick" : true,
16131         /**
16132          * @event contextmenu
16133          * Fires when a template node is right clicked.
16134          * @param {Roo.View} this
16135          * @param {Number} index The index of the target node
16136          * @param {HTMLElement} node The target node
16137          * @param {Roo.EventObject} e The raw event object
16138          */
16139             "contextmenu" : true,
16140         /**
16141          * @event selectionchange
16142          * Fires when the selected nodes change.
16143          * @param {Roo.View} this
16144          * @param {Array} selections Array of the selected nodes
16145          */
16146             "selectionchange" : true,
16147     
16148         /**
16149          * @event beforeselect
16150          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16151          * @param {Roo.View} this
16152          * @param {HTMLElement} node The node to be selected
16153          * @param {Array} selections Array of currently selected nodes
16154          */
16155             "beforeselect" : true,
16156         /**
16157          * @event preparedata
16158          * Fires on every row to render, to allow you to change the data.
16159          * @param {Roo.View} this
16160          * @param {Object} data to be rendered (change this)
16161          */
16162           "preparedata" : true
16163           
16164           
16165         });
16166
16167
16168
16169     this.el.on({
16170         "click": this.onClick,
16171         "dblclick": this.onDblClick,
16172         "contextmenu": this.onContextMenu,
16173         scope:this
16174     });
16175
16176     this.selections = [];
16177     this.nodes = [];
16178     this.cmp = new Roo.CompositeElementLite([]);
16179     if(this.store){
16180         this.store = Roo.factory(this.store, Roo.data);
16181         this.setStore(this.store, true);
16182     }
16183     
16184     if ( this.footer && this.footer.xtype) {
16185            
16186          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16187         
16188         this.footer.dataSource = this.store;
16189         this.footer.container = fctr;
16190         this.footer = Roo.factory(this.footer, Roo);
16191         fctr.insertFirst(this.el);
16192         
16193         // this is a bit insane - as the paging toolbar seems to detach the el..
16194 //        dom.parentNode.parentNode.parentNode
16195          // they get detached?
16196     }
16197     
16198     
16199     Roo.View.superclass.constructor.call(this);
16200     
16201     
16202 };
16203
16204 Roo.extend(Roo.View, Roo.util.Observable, {
16205     
16206      /**
16207      * @cfg {Roo.data.Store} store Data store to load data from.
16208      */
16209     store : false,
16210     
16211     /**
16212      * @cfg {String|Roo.Element} el The container element.
16213      */
16214     el : '',
16215     
16216     /**
16217      * @cfg {String|Roo.Template} tpl The template used by this View 
16218      */
16219     tpl : false,
16220     /**
16221      * @cfg {String} dataName the named area of the template to use as the data area
16222      *                          Works with domtemplates roo-name="name"
16223      */
16224     dataName: false,
16225     /**
16226      * @cfg {String} selectedClass The css class to add to selected nodes
16227      */
16228     selectedClass : "x-view-selected",
16229      /**
16230      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16231      */
16232     emptyText : "",
16233     
16234     /**
16235      * @cfg {String} text to display on mask (default Loading)
16236      */
16237     mask : false,
16238     /**
16239      * @cfg {Boolean} multiSelect Allow multiple selection
16240      */
16241     multiSelect : false,
16242     /**
16243      * @cfg {Boolean} singleSelect Allow single selection
16244      */
16245     singleSelect:  false,
16246     
16247     /**
16248      * @cfg {Boolean} toggleSelect - selecting 
16249      */
16250     toggleSelect : false,
16251     
16252     /**
16253      * @cfg {Boolean} tickable - selecting 
16254      */
16255     tickable : false,
16256     
16257     /**
16258      * Returns the element this view is bound to.
16259      * @return {Roo.Element}
16260      */
16261     getEl : function(){
16262         return this.wrapEl;
16263     },
16264     
16265     
16266
16267     /**
16268      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16269      */
16270     refresh : function(){
16271         //Roo.log('refresh');
16272         var t = this.tpl;
16273         
16274         // if we are using something like 'domtemplate', then
16275         // the what gets used is:
16276         // t.applySubtemplate(NAME, data, wrapping data..)
16277         // the outer template then get' applied with
16278         //     the store 'extra data'
16279         // and the body get's added to the
16280         //      roo-name="data" node?
16281         //      <span class='roo-tpl-{name}'></span> ?????
16282         
16283         
16284         
16285         this.clearSelections();
16286         this.el.update("");
16287         var html = [];
16288         var records = this.store.getRange();
16289         if(records.length < 1) {
16290             
16291             // is this valid??  = should it render a template??
16292             
16293             this.el.update(this.emptyText);
16294             return;
16295         }
16296         var el = this.el;
16297         if (this.dataName) {
16298             this.el.update(t.apply(this.store.meta)); //????
16299             el = this.el.child('.roo-tpl-' + this.dataName);
16300         }
16301         
16302         for(var i = 0, len = records.length; i < len; i++){
16303             var data = this.prepareData(records[i].data, i, records[i]);
16304             this.fireEvent("preparedata", this, data, i, records[i]);
16305             
16306             var d = Roo.apply({}, data);
16307             
16308             if(this.tickable){
16309                 Roo.apply(d, {'roo-id' : Roo.id()});
16310                 
16311                 var _this = this;
16312             
16313                 Roo.each(this.parent.item, function(item){
16314                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16315                         return;
16316                     }
16317                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16318                 });
16319             }
16320             
16321             html[html.length] = Roo.util.Format.trim(
16322                 this.dataName ?
16323                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16324                     t.apply(d)
16325             );
16326         }
16327         
16328         
16329         
16330         el.update(html.join(""));
16331         this.nodes = el.dom.childNodes;
16332         this.updateIndexes(0);
16333     },
16334     
16335
16336     /**
16337      * Function to override to reformat the data that is sent to
16338      * the template for each node.
16339      * DEPRICATED - use the preparedata event handler.
16340      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16341      * a JSON object for an UpdateManager bound view).
16342      */
16343     prepareData : function(data, index, record)
16344     {
16345         this.fireEvent("preparedata", this, data, index, record);
16346         return data;
16347     },
16348
16349     onUpdate : function(ds, record){
16350         // Roo.log('on update');   
16351         this.clearSelections();
16352         var index = this.store.indexOf(record);
16353         var n = this.nodes[index];
16354         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16355         n.parentNode.removeChild(n);
16356         this.updateIndexes(index, index);
16357     },
16358
16359     
16360     
16361 // --------- FIXME     
16362     onAdd : function(ds, records, index)
16363     {
16364         //Roo.log(['on Add', ds, records, index] );        
16365         this.clearSelections();
16366         if(this.nodes.length == 0){
16367             this.refresh();
16368             return;
16369         }
16370         var n = this.nodes[index];
16371         for(var i = 0, len = records.length; i < len; i++){
16372             var d = this.prepareData(records[i].data, i, records[i]);
16373             if(n){
16374                 this.tpl.insertBefore(n, d);
16375             }else{
16376                 
16377                 this.tpl.append(this.el, d);
16378             }
16379         }
16380         this.updateIndexes(index);
16381     },
16382
16383     onRemove : function(ds, record, index){
16384        // Roo.log('onRemove');
16385         this.clearSelections();
16386         var el = this.dataName  ?
16387             this.el.child('.roo-tpl-' + this.dataName) :
16388             this.el; 
16389         
16390         el.dom.removeChild(this.nodes[index]);
16391         this.updateIndexes(index);
16392     },
16393
16394     /**
16395      * Refresh an individual node.
16396      * @param {Number} index
16397      */
16398     refreshNode : function(index){
16399         this.onUpdate(this.store, this.store.getAt(index));
16400     },
16401
16402     updateIndexes : function(startIndex, endIndex){
16403         var ns = this.nodes;
16404         startIndex = startIndex || 0;
16405         endIndex = endIndex || ns.length - 1;
16406         for(var i = startIndex; i <= endIndex; i++){
16407             ns[i].nodeIndex = i;
16408         }
16409     },
16410
16411     /**
16412      * Changes the data store this view uses and refresh the view.
16413      * @param {Store} store
16414      */
16415     setStore : function(store, initial){
16416         if(!initial && this.store){
16417             this.store.un("datachanged", this.refresh);
16418             this.store.un("add", this.onAdd);
16419             this.store.un("remove", this.onRemove);
16420             this.store.un("update", this.onUpdate);
16421             this.store.un("clear", this.refresh);
16422             this.store.un("beforeload", this.onBeforeLoad);
16423             this.store.un("load", this.onLoad);
16424             this.store.un("loadexception", this.onLoad);
16425         }
16426         if(store){
16427           
16428             store.on("datachanged", this.refresh, this);
16429             store.on("add", this.onAdd, this);
16430             store.on("remove", this.onRemove, this);
16431             store.on("update", this.onUpdate, this);
16432             store.on("clear", this.refresh, this);
16433             store.on("beforeload", this.onBeforeLoad, this);
16434             store.on("load", this.onLoad, this);
16435             store.on("loadexception", this.onLoad, this);
16436         }
16437         
16438         if(store){
16439             this.refresh();
16440         }
16441     },
16442     /**
16443      * onbeforeLoad - masks the loading area.
16444      *
16445      */
16446     onBeforeLoad : function(store,opts)
16447     {
16448          //Roo.log('onBeforeLoad');   
16449         if (!opts.add) {
16450             this.el.update("");
16451         }
16452         this.el.mask(this.mask ? this.mask : "Loading" ); 
16453     },
16454     onLoad : function ()
16455     {
16456         this.el.unmask();
16457     },
16458     
16459
16460     /**
16461      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16462      * @param {HTMLElement} node
16463      * @return {HTMLElement} The template node
16464      */
16465     findItemFromChild : function(node){
16466         var el = this.dataName  ?
16467             this.el.child('.roo-tpl-' + this.dataName,true) :
16468             this.el.dom; 
16469         
16470         if(!node || node.parentNode == el){
16471                     return node;
16472             }
16473             var p = node.parentNode;
16474             while(p && p != el){
16475             if(p.parentNode == el){
16476                 return p;
16477             }
16478             p = p.parentNode;
16479         }
16480             return null;
16481     },
16482
16483     /** @ignore */
16484     onClick : function(e){
16485         var item = this.findItemFromChild(e.getTarget());
16486         if(item){
16487             var index = this.indexOf(item);
16488             if(this.onItemClick(item, index, e) !== false){
16489                 this.fireEvent("click", this, index, item, e);
16490             }
16491         }else{
16492             this.clearSelections();
16493         }
16494     },
16495
16496     /** @ignore */
16497     onContextMenu : function(e){
16498         var item = this.findItemFromChild(e.getTarget());
16499         if(item){
16500             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16501         }
16502     },
16503
16504     /** @ignore */
16505     onDblClick : function(e){
16506         var item = this.findItemFromChild(e.getTarget());
16507         if(item){
16508             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16509         }
16510     },
16511
16512     onItemClick : function(item, index, e)
16513     {
16514         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16515             return false;
16516         }
16517         if (this.toggleSelect) {
16518             var m = this.isSelected(item) ? 'unselect' : 'select';
16519             //Roo.log(m);
16520             var _t = this;
16521             _t[m](item, true, false);
16522             return true;
16523         }
16524         if(this.multiSelect || this.singleSelect){
16525             if(this.multiSelect && e.shiftKey && this.lastSelection){
16526                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16527             }else{
16528                 this.select(item, this.multiSelect && e.ctrlKey);
16529                 this.lastSelection = item;
16530             }
16531             
16532             if(!this.tickable){
16533                 e.preventDefault();
16534             }
16535             
16536         }
16537         return true;
16538     },
16539
16540     /**
16541      * Get the number of selected nodes.
16542      * @return {Number}
16543      */
16544     getSelectionCount : function(){
16545         return this.selections.length;
16546     },
16547
16548     /**
16549      * Get the currently selected nodes.
16550      * @return {Array} An array of HTMLElements
16551      */
16552     getSelectedNodes : function(){
16553         return this.selections;
16554     },
16555
16556     /**
16557      * Get the indexes of the selected nodes.
16558      * @return {Array}
16559      */
16560     getSelectedIndexes : function(){
16561         var indexes = [], s = this.selections;
16562         for(var i = 0, len = s.length; i < len; i++){
16563             indexes.push(s[i].nodeIndex);
16564         }
16565         return indexes;
16566     },
16567
16568     /**
16569      * Clear all selections
16570      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16571      */
16572     clearSelections : function(suppressEvent){
16573         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16574             this.cmp.elements = this.selections;
16575             this.cmp.removeClass(this.selectedClass);
16576             this.selections = [];
16577             if(!suppressEvent){
16578                 this.fireEvent("selectionchange", this, this.selections);
16579             }
16580         }
16581     },
16582
16583     /**
16584      * Returns true if the passed node is selected
16585      * @param {HTMLElement/Number} node The node or node index
16586      * @return {Boolean}
16587      */
16588     isSelected : function(node){
16589         var s = this.selections;
16590         if(s.length < 1){
16591             return false;
16592         }
16593         node = this.getNode(node);
16594         return s.indexOf(node) !== -1;
16595     },
16596
16597     /**
16598      * Selects nodes.
16599      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16600      * @param {Boolean} keepExisting (optional) true to keep existing selections
16601      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16602      */
16603     select : function(nodeInfo, keepExisting, suppressEvent){
16604         if(nodeInfo instanceof Array){
16605             if(!keepExisting){
16606                 this.clearSelections(true);
16607             }
16608             for(var i = 0, len = nodeInfo.length; i < len; i++){
16609                 this.select(nodeInfo[i], true, true);
16610             }
16611             return;
16612         } 
16613         var node = this.getNode(nodeInfo);
16614         if(!node || this.isSelected(node)){
16615             return; // already selected.
16616         }
16617         if(!keepExisting){
16618             this.clearSelections(true);
16619         }
16620         
16621         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16622             Roo.fly(node).addClass(this.selectedClass);
16623             this.selections.push(node);
16624             if(!suppressEvent){
16625                 this.fireEvent("selectionchange", this, this.selections);
16626             }
16627         }
16628         
16629         
16630     },
16631       /**
16632      * Unselects nodes.
16633      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16634      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16635      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16636      */
16637     unselect : function(nodeInfo, keepExisting, suppressEvent)
16638     {
16639         if(nodeInfo instanceof Array){
16640             Roo.each(this.selections, function(s) {
16641                 this.unselect(s, nodeInfo);
16642             }, this);
16643             return;
16644         }
16645         var node = this.getNode(nodeInfo);
16646         if(!node || !this.isSelected(node)){
16647             //Roo.log("not selected");
16648             return; // not selected.
16649         }
16650         // fireevent???
16651         var ns = [];
16652         Roo.each(this.selections, function(s) {
16653             if (s == node ) {
16654                 Roo.fly(node).removeClass(this.selectedClass);
16655
16656                 return;
16657             }
16658             ns.push(s);
16659         },this);
16660         
16661         this.selections= ns;
16662         this.fireEvent("selectionchange", this, this.selections);
16663     },
16664
16665     /**
16666      * Gets a template node.
16667      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16668      * @return {HTMLElement} The node or null if it wasn't found
16669      */
16670     getNode : function(nodeInfo){
16671         if(typeof nodeInfo == "string"){
16672             return document.getElementById(nodeInfo);
16673         }else if(typeof nodeInfo == "number"){
16674             return this.nodes[nodeInfo];
16675         }
16676         return nodeInfo;
16677     },
16678
16679     /**
16680      * Gets a range template nodes.
16681      * @param {Number} startIndex
16682      * @param {Number} endIndex
16683      * @return {Array} An array of nodes
16684      */
16685     getNodes : function(start, end){
16686         var ns = this.nodes;
16687         start = start || 0;
16688         end = typeof end == "undefined" ? ns.length - 1 : end;
16689         var nodes = [];
16690         if(start <= end){
16691             for(var i = start; i <= end; i++){
16692                 nodes.push(ns[i]);
16693             }
16694         } else{
16695             for(var i = start; i >= end; i--){
16696                 nodes.push(ns[i]);
16697             }
16698         }
16699         return nodes;
16700     },
16701
16702     /**
16703      * Finds the index of the passed node
16704      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16705      * @return {Number} The index of the node or -1
16706      */
16707     indexOf : function(node){
16708         node = this.getNode(node);
16709         if(typeof node.nodeIndex == "number"){
16710             return node.nodeIndex;
16711         }
16712         var ns = this.nodes;
16713         for(var i = 0, len = ns.length; i < len; i++){
16714             if(ns[i] == node){
16715                 return i;
16716             }
16717         }
16718         return -1;
16719     }
16720 });
16721 /*
16722  * - LGPL
16723  *
16724  * based on jquery fullcalendar
16725  * 
16726  */
16727
16728 Roo.bootstrap = Roo.bootstrap || {};
16729 /**
16730  * @class Roo.bootstrap.Calendar
16731  * @extends Roo.bootstrap.Component
16732  * Bootstrap Calendar class
16733  * @cfg {Boolean} loadMask (true|false) default false
16734  * @cfg {Object} header generate the user specific header of the calendar, default false
16735
16736  * @constructor
16737  * Create a new Container
16738  * @param {Object} config The config object
16739  */
16740
16741
16742
16743 Roo.bootstrap.Calendar = function(config){
16744     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16745      this.addEvents({
16746         /**
16747              * @event select
16748              * Fires when a date is selected
16749              * @param {DatePicker} this
16750              * @param {Date} date The selected date
16751              */
16752         'select': true,
16753         /**
16754              * @event monthchange
16755              * Fires when the displayed month changes 
16756              * @param {DatePicker} this
16757              * @param {Date} date The selected month
16758              */
16759         'monthchange': true,
16760         /**
16761              * @event evententer
16762              * Fires when mouse over an event
16763              * @param {Calendar} this
16764              * @param {event} Event
16765              */
16766         'evententer': true,
16767         /**
16768              * @event eventleave
16769              * Fires when the mouse leaves an
16770              * @param {Calendar} this
16771              * @param {event}
16772              */
16773         'eventleave': true,
16774         /**
16775              * @event eventclick
16776              * Fires when the mouse click an
16777              * @param {Calendar} this
16778              * @param {event}
16779              */
16780         'eventclick': true
16781         
16782     });
16783
16784 };
16785
16786 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16787     
16788      /**
16789      * @cfg {Number} startDay
16790      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16791      */
16792     startDay : 0,
16793     
16794     loadMask : false,
16795     
16796     header : false,
16797       
16798     getAutoCreate : function(){
16799         
16800         
16801         var fc_button = function(name, corner, style, content ) {
16802             return Roo.apply({},{
16803                 tag : 'span',
16804                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16805                          (corner.length ?
16806                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16807                             ''
16808                         ),
16809                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16810                 unselectable: 'on'
16811             });
16812         };
16813         
16814         var header = {};
16815         
16816         if(!this.header){
16817             header = {
16818                 tag : 'table',
16819                 cls : 'fc-header',
16820                 style : 'width:100%',
16821                 cn : [
16822                     {
16823                         tag: 'tr',
16824                         cn : [
16825                             {
16826                                 tag : 'td',
16827                                 cls : 'fc-header-left',
16828                                 cn : [
16829                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16830                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16831                                     { tag: 'span', cls: 'fc-header-space' },
16832                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16833
16834
16835                                 ]
16836                             },
16837
16838                             {
16839                                 tag : 'td',
16840                                 cls : 'fc-header-center',
16841                                 cn : [
16842                                     {
16843                                         tag: 'span',
16844                                         cls: 'fc-header-title',
16845                                         cn : {
16846                                             tag: 'H2',
16847                                             html : 'month / year'
16848                                         }
16849                                     }
16850
16851                                 ]
16852                             },
16853                             {
16854                                 tag : 'td',
16855                                 cls : 'fc-header-right',
16856                                 cn : [
16857                               /*      fc_button('month', 'left', '', 'month' ),
16858                                     fc_button('week', '', '', 'week' ),
16859                                     fc_button('day', 'right', '', 'day' )
16860                                 */    
16861
16862                                 ]
16863                             }
16864
16865                         ]
16866                     }
16867                 ]
16868             };
16869         }
16870         
16871         header = this.header;
16872         
16873        
16874         var cal_heads = function() {
16875             var ret = [];
16876             // fixme - handle this.
16877             
16878             for (var i =0; i < Date.dayNames.length; i++) {
16879                 var d = Date.dayNames[i];
16880                 ret.push({
16881                     tag: 'th',
16882                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16883                     html : d.substring(0,3)
16884                 });
16885                 
16886             }
16887             ret[0].cls += ' fc-first';
16888             ret[6].cls += ' fc-last';
16889             return ret;
16890         };
16891         var cal_cell = function(n) {
16892             return  {
16893                 tag: 'td',
16894                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16895                 cn : [
16896                     {
16897                         cn : [
16898                             {
16899                                 cls: 'fc-day-number',
16900                                 html: 'D'
16901                             },
16902                             {
16903                                 cls: 'fc-day-content',
16904                              
16905                                 cn : [
16906                                      {
16907                                         style: 'position: relative;' // height: 17px;
16908                                     }
16909                                 ]
16910                             }
16911                             
16912                             
16913                         ]
16914                     }
16915                 ]
16916                 
16917             }
16918         };
16919         var cal_rows = function() {
16920             
16921             var ret = [];
16922             for (var r = 0; r < 6; r++) {
16923                 var row= {
16924                     tag : 'tr',
16925                     cls : 'fc-week',
16926                     cn : []
16927                 };
16928                 
16929                 for (var i =0; i < Date.dayNames.length; i++) {
16930                     var d = Date.dayNames[i];
16931                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16932
16933                 }
16934                 row.cn[0].cls+=' fc-first';
16935                 row.cn[0].cn[0].style = 'min-height:90px';
16936                 row.cn[6].cls+=' fc-last';
16937                 ret.push(row);
16938                 
16939             }
16940             ret[0].cls += ' fc-first';
16941             ret[4].cls += ' fc-prev-last';
16942             ret[5].cls += ' fc-last';
16943             return ret;
16944             
16945         };
16946         
16947         var cal_table = {
16948             tag: 'table',
16949             cls: 'fc-border-separate',
16950             style : 'width:100%',
16951             cellspacing  : 0,
16952             cn : [
16953                 { 
16954                     tag: 'thead',
16955                     cn : [
16956                         { 
16957                             tag: 'tr',
16958                             cls : 'fc-first fc-last',
16959                             cn : cal_heads()
16960                         }
16961                     ]
16962                 },
16963                 { 
16964                     tag: 'tbody',
16965                     cn : cal_rows()
16966                 }
16967                   
16968             ]
16969         };
16970          
16971          var cfg = {
16972             cls : 'fc fc-ltr',
16973             cn : [
16974                 header,
16975                 {
16976                     cls : 'fc-content',
16977                     style : "position: relative;",
16978                     cn : [
16979                         {
16980                             cls : 'fc-view fc-view-month fc-grid',
16981                             style : 'position: relative',
16982                             unselectable : 'on',
16983                             cn : [
16984                                 {
16985                                     cls : 'fc-event-container',
16986                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16987                                 },
16988                                 cal_table
16989                             ]
16990                         }
16991                     ]
16992     
16993                 }
16994            ] 
16995             
16996         };
16997         
16998          
16999         
17000         return cfg;
17001     },
17002     
17003     
17004     initEvents : function()
17005     {
17006         if(!this.store){
17007             throw "can not find store for calendar";
17008         }
17009         
17010         var mark = {
17011             tag: "div",
17012             cls:"x-dlg-mask",
17013             style: "text-align:center",
17014             cn: [
17015                 {
17016                     tag: "div",
17017                     style: "background-color:white;width:50%;margin:250 auto",
17018                     cn: [
17019                         {
17020                             tag: "img",
17021                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17022                         },
17023                         {
17024                             tag: "span",
17025                             html: "Loading"
17026                         }
17027                         
17028                     ]
17029                 }
17030             ]
17031         };
17032         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17033         
17034         var size = this.el.select('.fc-content', true).first().getSize();
17035         this.maskEl.setSize(size.width, size.height);
17036         this.maskEl.enableDisplayMode("block");
17037         if(!this.loadMask){
17038             this.maskEl.hide();
17039         }
17040         
17041         this.store = Roo.factory(this.store, Roo.data);
17042         this.store.on('load', this.onLoad, this);
17043         this.store.on('beforeload', this.onBeforeLoad, this);
17044         
17045         this.resize();
17046         
17047         this.cells = this.el.select('.fc-day',true);
17048         //Roo.log(this.cells);
17049         this.textNodes = this.el.query('.fc-day-number');
17050         this.cells.addClassOnOver('fc-state-hover');
17051         
17052         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17053         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17054         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17055         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17056         
17057         this.on('monthchange', this.onMonthChange, this);
17058         
17059         this.update(new Date().clearTime());
17060     },
17061     
17062     resize : function() {
17063         var sz  = this.el.getSize();
17064         
17065         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17066         this.el.select('.fc-day-content div',true).setHeight(34);
17067     },
17068     
17069     
17070     // private
17071     showPrevMonth : function(e){
17072         this.update(this.activeDate.add("mo", -1));
17073     },
17074     showToday : function(e){
17075         this.update(new Date().clearTime());
17076     },
17077     // private
17078     showNextMonth : function(e){
17079         this.update(this.activeDate.add("mo", 1));
17080     },
17081
17082     // private
17083     showPrevYear : function(){
17084         this.update(this.activeDate.add("y", -1));
17085     },
17086
17087     // private
17088     showNextYear : function(){
17089         this.update(this.activeDate.add("y", 1));
17090     },
17091
17092     
17093    // private
17094     update : function(date)
17095     {
17096         var vd = this.activeDate;
17097         this.activeDate = date;
17098 //        if(vd && this.el){
17099 //            var t = date.getTime();
17100 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17101 //                Roo.log('using add remove');
17102 //                
17103 //                this.fireEvent('monthchange', this, date);
17104 //                
17105 //                this.cells.removeClass("fc-state-highlight");
17106 //                this.cells.each(function(c){
17107 //                   if(c.dateValue == t){
17108 //                       c.addClass("fc-state-highlight");
17109 //                       setTimeout(function(){
17110 //                            try{c.dom.firstChild.focus();}catch(e){}
17111 //                       }, 50);
17112 //                       return false;
17113 //                   }
17114 //                   return true;
17115 //                });
17116 //                return;
17117 //            }
17118 //        }
17119         
17120         var days = date.getDaysInMonth();
17121         
17122         var firstOfMonth = date.getFirstDateOfMonth();
17123         var startingPos = firstOfMonth.getDay()-this.startDay;
17124         
17125         if(startingPos < this.startDay){
17126             startingPos += 7;
17127         }
17128         
17129         var pm = date.add(Date.MONTH, -1);
17130         var prevStart = pm.getDaysInMonth()-startingPos;
17131 //        
17132         this.cells = this.el.select('.fc-day',true);
17133         this.textNodes = this.el.query('.fc-day-number');
17134         this.cells.addClassOnOver('fc-state-hover');
17135         
17136         var cells = this.cells.elements;
17137         var textEls = this.textNodes;
17138         
17139         Roo.each(cells, function(cell){
17140             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17141         });
17142         
17143         days += startingPos;
17144
17145         // convert everything to numbers so it's fast
17146         var day = 86400000;
17147         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17148         //Roo.log(d);
17149         //Roo.log(pm);
17150         //Roo.log(prevStart);
17151         
17152         var today = new Date().clearTime().getTime();
17153         var sel = date.clearTime().getTime();
17154         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17155         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17156         var ddMatch = this.disabledDatesRE;
17157         var ddText = this.disabledDatesText;
17158         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17159         var ddaysText = this.disabledDaysText;
17160         var format = this.format;
17161         
17162         var setCellClass = function(cal, cell){
17163             cell.row = 0;
17164             cell.events = [];
17165             cell.more = [];
17166             //Roo.log('set Cell Class');
17167             cell.title = "";
17168             var t = d.getTime();
17169             
17170             //Roo.log(d);
17171             
17172             cell.dateValue = t;
17173             if(t == today){
17174                 cell.className += " fc-today";
17175                 cell.className += " fc-state-highlight";
17176                 cell.title = cal.todayText;
17177             }
17178             if(t == sel){
17179                 // disable highlight in other month..
17180                 //cell.className += " fc-state-highlight";
17181                 
17182             }
17183             // disabling
17184             if(t < min) {
17185                 cell.className = " fc-state-disabled";
17186                 cell.title = cal.minText;
17187                 return;
17188             }
17189             if(t > max) {
17190                 cell.className = " fc-state-disabled";
17191                 cell.title = cal.maxText;
17192                 return;
17193             }
17194             if(ddays){
17195                 if(ddays.indexOf(d.getDay()) != -1){
17196                     cell.title = ddaysText;
17197                     cell.className = " fc-state-disabled";
17198                 }
17199             }
17200             if(ddMatch && format){
17201                 var fvalue = d.dateFormat(format);
17202                 if(ddMatch.test(fvalue)){
17203                     cell.title = ddText.replace("%0", fvalue);
17204                     cell.className = " fc-state-disabled";
17205                 }
17206             }
17207             
17208             if (!cell.initialClassName) {
17209                 cell.initialClassName = cell.dom.className;
17210             }
17211             
17212             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17213         };
17214
17215         var i = 0;
17216         
17217         for(; i < startingPos; i++) {
17218             textEls[i].innerHTML = (++prevStart);
17219             d.setDate(d.getDate()+1);
17220             
17221             cells[i].className = "fc-past fc-other-month";
17222             setCellClass(this, cells[i]);
17223         }
17224         
17225         var intDay = 0;
17226         
17227         for(; i < days; i++){
17228             intDay = i - startingPos + 1;
17229             textEls[i].innerHTML = (intDay);
17230             d.setDate(d.getDate()+1);
17231             
17232             cells[i].className = ''; // "x-date-active";
17233             setCellClass(this, cells[i]);
17234         }
17235         var extraDays = 0;
17236         
17237         for(; i < 42; i++) {
17238             textEls[i].innerHTML = (++extraDays);
17239             d.setDate(d.getDate()+1);
17240             
17241             cells[i].className = "fc-future fc-other-month";
17242             setCellClass(this, cells[i]);
17243         }
17244         
17245         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17246         
17247         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17248         
17249         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17250         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17251         
17252         if(totalRows != 6){
17253             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17254             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17255         }
17256         
17257         this.fireEvent('monthchange', this, date);
17258         
17259         
17260         /*
17261         if(!this.internalRender){
17262             var main = this.el.dom.firstChild;
17263             var w = main.offsetWidth;
17264             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17265             Roo.fly(main).setWidth(w);
17266             this.internalRender = true;
17267             // opera does not respect the auto grow header center column
17268             // then, after it gets a width opera refuses to recalculate
17269             // without a second pass
17270             if(Roo.isOpera && !this.secondPass){
17271                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17272                 this.secondPass = true;
17273                 this.update.defer(10, this, [date]);
17274             }
17275         }
17276         */
17277         
17278     },
17279     
17280     findCell : function(dt) {
17281         dt = dt.clearTime().getTime();
17282         var ret = false;
17283         this.cells.each(function(c){
17284             //Roo.log("check " +c.dateValue + '?=' + dt);
17285             if(c.dateValue == dt){
17286                 ret = c;
17287                 return false;
17288             }
17289             return true;
17290         });
17291         
17292         return ret;
17293     },
17294     
17295     findCells : function(ev) {
17296         var s = ev.start.clone().clearTime().getTime();
17297        // Roo.log(s);
17298         var e= ev.end.clone().clearTime().getTime();
17299        // Roo.log(e);
17300         var ret = [];
17301         this.cells.each(function(c){
17302              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17303             
17304             if(c.dateValue > e){
17305                 return ;
17306             }
17307             if(c.dateValue < s){
17308                 return ;
17309             }
17310             ret.push(c);
17311         });
17312         
17313         return ret;    
17314     },
17315     
17316 //    findBestRow: function(cells)
17317 //    {
17318 //        var ret = 0;
17319 //        
17320 //        for (var i =0 ; i < cells.length;i++) {
17321 //            ret  = Math.max(cells[i].rows || 0,ret);
17322 //        }
17323 //        return ret;
17324 //        
17325 //    },
17326     
17327     
17328     addItem : function(ev)
17329     {
17330         // look for vertical location slot in
17331         var cells = this.findCells(ev);
17332         
17333 //        ev.row = this.findBestRow(cells);
17334         
17335         // work out the location.
17336         
17337         var crow = false;
17338         var rows = [];
17339         for(var i =0; i < cells.length; i++) {
17340             
17341             cells[i].row = cells[0].row;
17342             
17343             if(i == 0){
17344                 cells[i].row = cells[i].row + 1;
17345             }
17346             
17347             if (!crow) {
17348                 crow = {
17349                     start : cells[i],
17350                     end :  cells[i]
17351                 };
17352                 continue;
17353             }
17354             if (crow.start.getY() == cells[i].getY()) {
17355                 // on same row.
17356                 crow.end = cells[i];
17357                 continue;
17358             }
17359             // different row.
17360             rows.push(crow);
17361             crow = {
17362                 start: cells[i],
17363                 end : cells[i]
17364             };
17365             
17366         }
17367         
17368         rows.push(crow);
17369         ev.els = [];
17370         ev.rows = rows;
17371         ev.cells = cells;
17372         
17373         cells[0].events.push(ev);
17374         
17375         this.calevents.push(ev);
17376     },
17377     
17378     clearEvents: function() {
17379         
17380         if(!this.calevents){
17381             return;
17382         }
17383         
17384         Roo.each(this.cells.elements, function(c){
17385             c.row = 0;
17386             c.events = [];
17387             c.more = [];
17388         });
17389         
17390         Roo.each(this.calevents, function(e) {
17391             Roo.each(e.els, function(el) {
17392                 el.un('mouseenter' ,this.onEventEnter, this);
17393                 el.un('mouseleave' ,this.onEventLeave, this);
17394                 el.remove();
17395             },this);
17396         },this);
17397         
17398         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17399             e.remove();
17400         });
17401         
17402     },
17403     
17404     renderEvents: function()
17405     {   
17406         var _this = this;
17407         
17408         this.cells.each(function(c) {
17409             
17410             if(c.row < 5){
17411                 return;
17412             }
17413             
17414             var ev = c.events;
17415             
17416             var r = 4;
17417             if(c.row != c.events.length){
17418                 r = 4 - (4 - (c.row - c.events.length));
17419             }
17420             
17421             c.events = ev.slice(0, r);
17422             c.more = ev.slice(r);
17423             
17424             if(c.more.length && c.more.length == 1){
17425                 c.events.push(c.more.pop());
17426             }
17427             
17428             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17429             
17430         });
17431             
17432         this.cells.each(function(c) {
17433             
17434             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17435             
17436             
17437             for (var e = 0; e < c.events.length; e++){
17438                 var ev = c.events[e];
17439                 var rows = ev.rows;
17440                 
17441                 for(var i = 0; i < rows.length; i++) {
17442                 
17443                     // how many rows should it span..
17444
17445                     var  cfg = {
17446                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17447                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17448
17449                         unselectable : "on",
17450                         cn : [
17451                             {
17452                                 cls: 'fc-event-inner',
17453                                 cn : [
17454     //                                {
17455     //                                  tag:'span',
17456     //                                  cls: 'fc-event-time',
17457     //                                  html : cells.length > 1 ? '' : ev.time
17458     //                                },
17459                                     {
17460                                       tag:'span',
17461                                       cls: 'fc-event-title',
17462                                       html : String.format('{0}', ev.title)
17463                                     }
17464
17465
17466                                 ]
17467                             },
17468                             {
17469                                 cls: 'ui-resizable-handle ui-resizable-e',
17470                                 html : '&nbsp;&nbsp;&nbsp'
17471                             }
17472
17473                         ]
17474                     };
17475
17476                     if (i == 0) {
17477                         cfg.cls += ' fc-event-start';
17478                     }
17479                     if ((i+1) == rows.length) {
17480                         cfg.cls += ' fc-event-end';
17481                     }
17482
17483                     var ctr = _this.el.select('.fc-event-container',true).first();
17484                     var cg = ctr.createChild(cfg);
17485
17486                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17487                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17488
17489                     var r = (c.more.length) ? 1 : 0;
17490                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17491                     cg.setWidth(ebox.right - sbox.x -2);
17492
17493                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17494                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17495                     cg.on('click', _this.onEventClick, _this, ev);
17496
17497                     ev.els.push(cg);
17498                     
17499                 }
17500                 
17501             }
17502             
17503             
17504             if(c.more.length){
17505                 var  cfg = {
17506                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17507                     style : 'position: absolute',
17508                     unselectable : "on",
17509                     cn : [
17510                         {
17511                             cls: 'fc-event-inner',
17512                             cn : [
17513                                 {
17514                                   tag:'span',
17515                                   cls: 'fc-event-title',
17516                                   html : 'More'
17517                                 }
17518
17519
17520                             ]
17521                         },
17522                         {
17523                             cls: 'ui-resizable-handle ui-resizable-e',
17524                             html : '&nbsp;&nbsp;&nbsp'
17525                         }
17526
17527                     ]
17528                 };
17529
17530                 var ctr = _this.el.select('.fc-event-container',true).first();
17531                 var cg = ctr.createChild(cfg);
17532
17533                 var sbox = c.select('.fc-day-content',true).first().getBox();
17534                 var ebox = c.select('.fc-day-content',true).first().getBox();
17535                 //Roo.log(cg);
17536                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17537                 cg.setWidth(ebox.right - sbox.x -2);
17538
17539                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17540                 
17541             }
17542             
17543         });
17544         
17545         
17546         
17547     },
17548     
17549     onEventEnter: function (e, el,event,d) {
17550         this.fireEvent('evententer', this, el, event);
17551     },
17552     
17553     onEventLeave: function (e, el,event,d) {
17554         this.fireEvent('eventleave', this, el, event);
17555     },
17556     
17557     onEventClick: function (e, el,event,d) {
17558         this.fireEvent('eventclick', this, el, event);
17559     },
17560     
17561     onMonthChange: function () {
17562         this.store.load();
17563     },
17564     
17565     onMoreEventClick: function(e, el, more)
17566     {
17567         var _this = this;
17568         
17569         this.calpopover.placement = 'right';
17570         this.calpopover.setTitle('More');
17571         
17572         this.calpopover.setContent('');
17573         
17574         var ctr = this.calpopover.el.select('.popover-content', true).first();
17575         
17576         Roo.each(more, function(m){
17577             var cfg = {
17578                 cls : 'fc-event-hori fc-event-draggable',
17579                 html : m.title
17580             };
17581             var cg = ctr.createChild(cfg);
17582             
17583             cg.on('click', _this.onEventClick, _this, m);
17584         });
17585         
17586         this.calpopover.show(el);
17587         
17588         
17589     },
17590     
17591     onLoad: function () 
17592     {   
17593         this.calevents = [];
17594         var cal = this;
17595         
17596         if(this.store.getCount() > 0){
17597             this.store.data.each(function(d){
17598                cal.addItem({
17599                     id : d.data.id,
17600                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17601                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17602                     time : d.data.start_time,
17603                     title : d.data.title,
17604                     description : d.data.description,
17605                     venue : d.data.venue
17606                 });
17607             });
17608         }
17609         
17610         this.renderEvents();
17611         
17612         if(this.calevents.length && this.loadMask){
17613             this.maskEl.hide();
17614         }
17615     },
17616     
17617     onBeforeLoad: function()
17618     {
17619         this.clearEvents();
17620         if(this.loadMask){
17621             this.maskEl.show();
17622         }
17623     }
17624 });
17625
17626  
17627  /*
17628  * - LGPL
17629  *
17630  * element
17631  * 
17632  */
17633
17634 /**
17635  * @class Roo.bootstrap.Popover
17636  * @extends Roo.bootstrap.Component
17637  * Bootstrap Popover class
17638  * @cfg {String} html contents of the popover   (or false to use children..)
17639  * @cfg {String} title of popover (or false to hide)
17640  * @cfg {String} placement how it is placed
17641  * @cfg {String} trigger click || hover (or false to trigger manually)
17642  * @cfg {String} over what (parent or false to trigger manually.)
17643  * @cfg {Number} delay - delay before showing
17644  
17645  * @constructor
17646  * Create a new Popover
17647  * @param {Object} config The config object
17648  */
17649
17650 Roo.bootstrap.Popover = function(config){
17651     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17652     
17653     this.addEvents({
17654         // raw events
17655          /**
17656          * @event show
17657          * After the popover show
17658          * 
17659          * @param {Roo.bootstrap.Popover} this
17660          */
17661         "show" : true,
17662         /**
17663          * @event hide
17664          * After the popover hide
17665          * 
17666          * @param {Roo.bootstrap.Popover} this
17667          */
17668         "hide" : true
17669     });
17670 };
17671
17672 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17673     
17674     title: 'Fill in a title',
17675     html: false,
17676     
17677     placement : 'right',
17678     trigger : 'hover', // hover
17679     
17680     delay : 0,
17681     
17682     over: 'parent',
17683     
17684     can_build_overlaid : false,
17685     
17686     getChildContainer : function()
17687     {
17688         return this.el.select('.popover-content',true).first();
17689     },
17690     
17691     getAutoCreate : function(){
17692          
17693         var cfg = {
17694            cls : 'popover roo-dynamic',
17695            style: 'display:block',
17696            cn : [
17697                 {
17698                     cls : 'arrow'
17699                 },
17700                 {
17701                     cls : 'popover-inner',
17702                     cn : [
17703                         {
17704                             tag: 'h3',
17705                             cls: 'popover-title popover-header',
17706                             html : this.title
17707                         },
17708                         {
17709                             cls : 'popover-content popover-body',
17710                             html : this.html
17711                         }
17712                     ]
17713                     
17714                 }
17715            ]
17716         };
17717         
17718         return cfg;
17719     },
17720     setTitle: function(str)
17721     {
17722         this.title = str;
17723         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17724     },
17725     setContent: function(str)
17726     {
17727         this.html = str;
17728         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17729     },
17730     // as it get's added to the bottom of the page.
17731     onRender : function(ct, position)
17732     {
17733         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17734         if(!this.el){
17735             var cfg = Roo.apply({},  this.getAutoCreate());
17736             cfg.id = Roo.id();
17737             
17738             if (this.cls) {
17739                 cfg.cls += ' ' + this.cls;
17740             }
17741             if (this.style) {
17742                 cfg.style = this.style;
17743             }
17744             //Roo.log("adding to ");
17745             this.el = Roo.get(document.body).createChild(cfg, position);
17746 //            Roo.log(this.el);
17747         }
17748         this.initEvents();
17749     },
17750     
17751     initEvents : function()
17752     {
17753         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17754         this.el.enableDisplayMode('block');
17755         this.el.hide();
17756         if (this.over === false) {
17757             return; 
17758         }
17759         if (this.triggers === false) {
17760             return;
17761         }
17762         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17763         var triggers = this.trigger ? this.trigger.split(' ') : [];
17764         Roo.each(triggers, function(trigger) {
17765         
17766             if (trigger == 'click') {
17767                 on_el.on('click', this.toggle, this);
17768             } else if (trigger != 'manual') {
17769                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17770                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17771       
17772                 on_el.on(eventIn  ,this.enter, this);
17773                 on_el.on(eventOut, this.leave, this);
17774             }
17775         }, this);
17776         
17777     },
17778     
17779     
17780     // private
17781     timeout : null,
17782     hoverState : null,
17783     
17784     toggle : function () {
17785         this.hoverState == 'in' ? this.leave() : this.enter();
17786     },
17787     
17788     enter : function () {
17789         
17790         clearTimeout(this.timeout);
17791     
17792         this.hoverState = 'in';
17793     
17794         if (!this.delay || !this.delay.show) {
17795             this.show();
17796             return;
17797         }
17798         var _t = this;
17799         this.timeout = setTimeout(function () {
17800             if (_t.hoverState == 'in') {
17801                 _t.show();
17802             }
17803         }, this.delay.show)
17804     },
17805     
17806     leave : function() {
17807         clearTimeout(this.timeout);
17808     
17809         this.hoverState = 'out';
17810     
17811         if (!this.delay || !this.delay.hide) {
17812             this.hide();
17813             return;
17814         }
17815         var _t = this;
17816         this.timeout = setTimeout(function () {
17817             if (_t.hoverState == 'out') {
17818                 _t.hide();
17819             }
17820         }, this.delay.hide)
17821     },
17822     
17823     show : function (on_el)
17824     {
17825         if (!on_el) {
17826             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17827         }
17828         
17829         // set content.
17830         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17831         if (this.html !== false) {
17832             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17833         }
17834         this.el.removeClass([
17835             'fade','top','bottom', 'left', 'right','in',
17836             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17837         ]);
17838         if (!this.title.length) {
17839             this.el.select('.popover-title',true).hide();
17840         }
17841         
17842         var placement = typeof this.placement == 'function' ?
17843             this.placement.call(this, this.el, on_el) :
17844             this.placement;
17845             
17846         var autoToken = /\s?auto?\s?/i;
17847         var autoPlace = autoToken.test(placement);
17848         if (autoPlace) {
17849             placement = placement.replace(autoToken, '') || 'top';
17850         }
17851         
17852         //this.el.detach()
17853         //this.el.setXY([0,0]);
17854         this.el.show();
17855         this.el.dom.style.display='block';
17856         this.el.addClass(placement);
17857         
17858         //this.el.appendTo(on_el);
17859         
17860         var p = this.getPosition();
17861         var box = this.el.getBox();
17862         
17863         if (autoPlace) {
17864             // fixme..
17865         }
17866         var align = Roo.bootstrap.Popover.alignment[placement];
17867         
17868 //        Roo.log(align);
17869         this.el.alignTo(on_el, align[0],align[1]);
17870         //var arrow = this.el.select('.arrow',true).first();
17871         //arrow.set(align[2], 
17872         
17873         this.el.addClass('in');
17874         
17875         
17876         if (this.el.hasClass('fade')) {
17877             // fade it?
17878         }
17879         
17880         this.hoverState = 'in';
17881         
17882         this.fireEvent('show', this);
17883         
17884     },
17885     hide : function()
17886     {
17887         this.el.setXY([0,0]);
17888         this.el.removeClass('in');
17889         this.el.hide();
17890         this.hoverState = null;
17891         
17892         this.fireEvent('hide', this);
17893     }
17894     
17895 });
17896
17897 Roo.bootstrap.Popover.alignment = {
17898     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17899     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17900     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17901     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17902 };
17903
17904  /*
17905  * - LGPL
17906  *
17907  * Progress
17908  * 
17909  */
17910
17911 /**
17912  * @class Roo.bootstrap.Progress
17913  * @extends Roo.bootstrap.Component
17914  * Bootstrap Progress class
17915  * @cfg {Boolean} striped striped of the progress bar
17916  * @cfg {Boolean} active animated of the progress bar
17917  * 
17918  * 
17919  * @constructor
17920  * Create a new Progress
17921  * @param {Object} config The config object
17922  */
17923
17924 Roo.bootstrap.Progress = function(config){
17925     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17926 };
17927
17928 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17929     
17930     striped : false,
17931     active: false,
17932     
17933     getAutoCreate : function(){
17934         var cfg = {
17935             tag: 'div',
17936             cls: 'progress'
17937         };
17938         
17939         
17940         if(this.striped){
17941             cfg.cls += ' progress-striped';
17942         }
17943       
17944         if(this.active){
17945             cfg.cls += ' active';
17946         }
17947         
17948         
17949         return cfg;
17950     }
17951    
17952 });
17953
17954  
17955
17956  /*
17957  * - LGPL
17958  *
17959  * ProgressBar
17960  * 
17961  */
17962
17963 /**
17964  * @class Roo.bootstrap.ProgressBar
17965  * @extends Roo.bootstrap.Component
17966  * Bootstrap ProgressBar class
17967  * @cfg {Number} aria_valuenow aria-value now
17968  * @cfg {Number} aria_valuemin aria-value min
17969  * @cfg {Number} aria_valuemax aria-value max
17970  * @cfg {String} label label for the progress bar
17971  * @cfg {String} panel (success | info | warning | danger )
17972  * @cfg {String} role role of the progress bar
17973  * @cfg {String} sr_only text
17974  * 
17975  * 
17976  * @constructor
17977  * Create a new ProgressBar
17978  * @param {Object} config The config object
17979  */
17980
17981 Roo.bootstrap.ProgressBar = function(config){
17982     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17983 };
17984
17985 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17986     
17987     aria_valuenow : 0,
17988     aria_valuemin : 0,
17989     aria_valuemax : 100,
17990     label : false,
17991     panel : false,
17992     role : false,
17993     sr_only: false,
17994     
17995     getAutoCreate : function()
17996     {
17997         
17998         var cfg = {
17999             tag: 'div',
18000             cls: 'progress-bar',
18001             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18002         };
18003         
18004         if(this.sr_only){
18005             cfg.cn = {
18006                 tag: 'span',
18007                 cls: 'sr-only',
18008                 html: this.sr_only
18009             }
18010         }
18011         
18012         if(this.role){
18013             cfg.role = this.role;
18014         }
18015         
18016         if(this.aria_valuenow){
18017             cfg['aria-valuenow'] = this.aria_valuenow;
18018         }
18019         
18020         if(this.aria_valuemin){
18021             cfg['aria-valuemin'] = this.aria_valuemin;
18022         }
18023         
18024         if(this.aria_valuemax){
18025             cfg['aria-valuemax'] = this.aria_valuemax;
18026         }
18027         
18028         if(this.label && !this.sr_only){
18029             cfg.html = this.label;
18030         }
18031         
18032         if(this.panel){
18033             cfg.cls += ' progress-bar-' + this.panel;
18034         }
18035         
18036         return cfg;
18037     },
18038     
18039     update : function(aria_valuenow)
18040     {
18041         this.aria_valuenow = aria_valuenow;
18042         
18043         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18044     }
18045    
18046 });
18047
18048  
18049
18050  /*
18051  * - LGPL
18052  *
18053  * column
18054  * 
18055  */
18056
18057 /**
18058  * @class Roo.bootstrap.TabGroup
18059  * @extends Roo.bootstrap.Column
18060  * Bootstrap Column class
18061  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18062  * @cfg {Boolean} carousel true to make the group behave like a carousel
18063  * @cfg {Boolean} bullets show bullets for the panels
18064  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18065  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18066  * @cfg {Boolean} showarrow (true|false) show arrow default true
18067  * 
18068  * @constructor
18069  * Create a new TabGroup
18070  * @param {Object} config The config object
18071  */
18072
18073 Roo.bootstrap.TabGroup = function(config){
18074     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18075     if (!this.navId) {
18076         this.navId = Roo.id();
18077     }
18078     this.tabs = [];
18079     Roo.bootstrap.TabGroup.register(this);
18080     
18081 };
18082
18083 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18084     
18085     carousel : false,
18086     transition : false,
18087     bullets : 0,
18088     timer : 0,
18089     autoslide : false,
18090     slideFn : false,
18091     slideOnTouch : false,
18092     showarrow : true,
18093     
18094     getAutoCreate : function()
18095     {
18096         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18097         
18098         cfg.cls += ' tab-content';
18099         
18100         if (this.carousel) {
18101             cfg.cls += ' carousel slide';
18102             
18103             cfg.cn = [{
18104                cls : 'carousel-inner',
18105                cn : []
18106             }];
18107         
18108             if(this.bullets  && !Roo.isTouch){
18109                 
18110                 var bullets = {
18111                     cls : 'carousel-bullets',
18112                     cn : []
18113                 };
18114                
18115                 if(this.bullets_cls){
18116                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18117                 }
18118                 
18119                 bullets.cn.push({
18120                     cls : 'clear'
18121                 });
18122                 
18123                 cfg.cn[0].cn.push(bullets);
18124             }
18125             
18126             if(this.showarrow){
18127                 cfg.cn[0].cn.push({
18128                     tag : 'div',
18129                     class : 'carousel-arrow',
18130                     cn : [
18131                         {
18132                             tag : 'div',
18133                             class : 'carousel-prev',
18134                             cn : [
18135                                 {
18136                                     tag : 'i',
18137                                     class : 'fa fa-chevron-left'
18138                                 }
18139                             ]
18140                         },
18141                         {
18142                             tag : 'div',
18143                             class : 'carousel-next',
18144                             cn : [
18145                                 {
18146                                     tag : 'i',
18147                                     class : 'fa fa-chevron-right'
18148                                 }
18149                             ]
18150                         }
18151                     ]
18152                 });
18153             }
18154             
18155         }
18156         
18157         return cfg;
18158     },
18159     
18160     initEvents:  function()
18161     {
18162 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18163 //            this.el.on("touchstart", this.onTouchStart, this);
18164 //        }
18165         
18166         if(this.autoslide){
18167             var _this = this;
18168             
18169             this.slideFn = window.setInterval(function() {
18170                 _this.showPanelNext();
18171             }, this.timer);
18172         }
18173         
18174         if(this.showarrow){
18175             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18176             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18177         }
18178         
18179         
18180     },
18181     
18182 //    onTouchStart : function(e, el, o)
18183 //    {
18184 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18185 //            return;
18186 //        }
18187 //        
18188 //        this.showPanelNext();
18189 //    },
18190     
18191     
18192     getChildContainer : function()
18193     {
18194         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18195     },
18196     
18197     /**
18198     * register a Navigation item
18199     * @param {Roo.bootstrap.NavItem} the navitem to add
18200     */
18201     register : function(item)
18202     {
18203         this.tabs.push( item);
18204         item.navId = this.navId; // not really needed..
18205         this.addBullet();
18206     
18207     },
18208     
18209     getActivePanel : function()
18210     {
18211         var r = false;
18212         Roo.each(this.tabs, function(t) {
18213             if (t.active) {
18214                 r = t;
18215                 return false;
18216             }
18217             return null;
18218         });
18219         return r;
18220         
18221     },
18222     getPanelByName : function(n)
18223     {
18224         var r = false;
18225         Roo.each(this.tabs, function(t) {
18226             if (t.tabId == n) {
18227                 r = t;
18228                 return false;
18229             }
18230             return null;
18231         });
18232         return r;
18233     },
18234     indexOfPanel : function(p)
18235     {
18236         var r = false;
18237         Roo.each(this.tabs, function(t,i) {
18238             if (t.tabId == p.tabId) {
18239                 r = i;
18240                 return false;
18241             }
18242             return null;
18243         });
18244         return r;
18245     },
18246     /**
18247      * show a specific panel
18248      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18249      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18250      */
18251     showPanel : function (pan)
18252     {
18253         if(this.transition || typeof(pan) == 'undefined'){
18254             Roo.log("waiting for the transitionend");
18255             return;
18256         }
18257         
18258         if (typeof(pan) == 'number') {
18259             pan = this.tabs[pan];
18260         }
18261         
18262         if (typeof(pan) == 'string') {
18263             pan = this.getPanelByName(pan);
18264         }
18265         
18266         var cur = this.getActivePanel();
18267         
18268         if(!pan || !cur){
18269             Roo.log('pan or acitve pan is undefined');
18270             return false;
18271         }
18272         
18273         if (pan.tabId == this.getActivePanel().tabId) {
18274             return true;
18275         }
18276         
18277         if (false === cur.fireEvent('beforedeactivate')) {
18278             return false;
18279         }
18280         
18281         if(this.bullets > 0 && !Roo.isTouch){
18282             this.setActiveBullet(this.indexOfPanel(pan));
18283         }
18284         
18285         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18286             
18287             this.transition = true;
18288             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18289             var lr = dir == 'next' ? 'left' : 'right';
18290             pan.el.addClass(dir); // or prev
18291             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18292             cur.el.addClass(lr); // or right
18293             pan.el.addClass(lr);
18294             
18295             var _this = this;
18296             cur.el.on('transitionend', function() {
18297                 Roo.log("trans end?");
18298                 
18299                 pan.el.removeClass([lr,dir]);
18300                 pan.setActive(true);
18301                 
18302                 cur.el.removeClass([lr]);
18303                 cur.setActive(false);
18304                 
18305                 _this.transition = false;
18306                 
18307             }, this, { single:  true } );
18308             
18309             return true;
18310         }
18311         
18312         cur.setActive(false);
18313         pan.setActive(true);
18314         
18315         return true;
18316         
18317     },
18318     showPanelNext : function()
18319     {
18320         var i = this.indexOfPanel(this.getActivePanel());
18321         
18322         if (i >= this.tabs.length - 1 && !this.autoslide) {
18323             return;
18324         }
18325         
18326         if (i >= this.tabs.length - 1 && this.autoslide) {
18327             i = -1;
18328         }
18329         
18330         this.showPanel(this.tabs[i+1]);
18331     },
18332     
18333     showPanelPrev : function()
18334     {
18335         var i = this.indexOfPanel(this.getActivePanel());
18336         
18337         if (i  < 1 && !this.autoslide) {
18338             return;
18339         }
18340         
18341         if (i < 1 && this.autoslide) {
18342             i = this.tabs.length;
18343         }
18344         
18345         this.showPanel(this.tabs[i-1]);
18346     },
18347     
18348     
18349     addBullet: function()
18350     {
18351         if(!this.bullets || Roo.isTouch){
18352             return;
18353         }
18354         var ctr = this.el.select('.carousel-bullets',true).first();
18355         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18356         var bullet = ctr.createChild({
18357             cls : 'bullet bullet-' + i
18358         },ctr.dom.lastChild);
18359         
18360         
18361         var _this = this;
18362         
18363         bullet.on('click', (function(e, el, o, ii, t){
18364
18365             e.preventDefault();
18366
18367             this.showPanel(ii);
18368
18369             if(this.autoslide && this.slideFn){
18370                 clearInterval(this.slideFn);
18371                 this.slideFn = window.setInterval(function() {
18372                     _this.showPanelNext();
18373                 }, this.timer);
18374             }
18375
18376         }).createDelegate(this, [i, bullet], true));
18377                 
18378         
18379     },
18380      
18381     setActiveBullet : function(i)
18382     {
18383         if(Roo.isTouch){
18384             return;
18385         }
18386         
18387         Roo.each(this.el.select('.bullet', true).elements, function(el){
18388             el.removeClass('selected');
18389         });
18390
18391         var bullet = this.el.select('.bullet-' + i, true).first();
18392         
18393         if(!bullet){
18394             return;
18395         }
18396         
18397         bullet.addClass('selected');
18398     }
18399     
18400     
18401   
18402 });
18403
18404  
18405
18406  
18407  
18408 Roo.apply(Roo.bootstrap.TabGroup, {
18409     
18410     groups: {},
18411      /**
18412     * register a Navigation Group
18413     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18414     */
18415     register : function(navgrp)
18416     {
18417         this.groups[navgrp.navId] = navgrp;
18418         
18419     },
18420     /**
18421     * fetch a Navigation Group based on the navigation ID
18422     * if one does not exist , it will get created.
18423     * @param {string} the navgroup to add
18424     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18425     */
18426     get: function(navId) {
18427         if (typeof(this.groups[navId]) == 'undefined') {
18428             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18429         }
18430         return this.groups[navId] ;
18431     }
18432     
18433     
18434     
18435 });
18436
18437  /*
18438  * - LGPL
18439  *
18440  * TabPanel
18441  * 
18442  */
18443
18444 /**
18445  * @class Roo.bootstrap.TabPanel
18446  * @extends Roo.bootstrap.Component
18447  * Bootstrap TabPanel class
18448  * @cfg {Boolean} active panel active
18449  * @cfg {String} html panel content
18450  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18451  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18452  * @cfg {String} href click to link..
18453  * 
18454  * 
18455  * @constructor
18456  * Create a new TabPanel
18457  * @param {Object} config The config object
18458  */
18459
18460 Roo.bootstrap.TabPanel = function(config){
18461     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18462     this.addEvents({
18463         /**
18464              * @event changed
18465              * Fires when the active status changes
18466              * @param {Roo.bootstrap.TabPanel} this
18467              * @param {Boolean} state the new state
18468             
18469          */
18470         'changed': true,
18471         /**
18472              * @event beforedeactivate
18473              * Fires before a tab is de-activated - can be used to do validation on a form.
18474              * @param {Roo.bootstrap.TabPanel} this
18475              * @return {Boolean} false if there is an error
18476             
18477          */
18478         'beforedeactivate': true
18479      });
18480     
18481     this.tabId = this.tabId || Roo.id();
18482   
18483 };
18484
18485 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18486     
18487     active: false,
18488     html: false,
18489     tabId: false,
18490     navId : false,
18491     href : '',
18492     
18493     getAutoCreate : function(){
18494         var cfg = {
18495             tag: 'div',
18496             // item is needed for carousel - not sure if it has any effect otherwise
18497             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18498             html: this.html || ''
18499         };
18500         
18501         if(this.active){
18502             cfg.cls += ' active';
18503         }
18504         
18505         if(this.tabId){
18506             cfg.tabId = this.tabId;
18507         }
18508         
18509         
18510         return cfg;
18511     },
18512     
18513     initEvents:  function()
18514     {
18515         var p = this.parent();
18516         
18517         this.navId = this.navId || p.navId;
18518         
18519         if (typeof(this.navId) != 'undefined') {
18520             // not really needed.. but just in case.. parent should be a NavGroup.
18521             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18522             
18523             tg.register(this);
18524             
18525             var i = tg.tabs.length - 1;
18526             
18527             if(this.active && tg.bullets > 0 && i < tg.bullets){
18528                 tg.setActiveBullet(i);
18529             }
18530         }
18531         
18532         this.el.on('click', this.onClick, this);
18533         
18534         if(Roo.isTouch){
18535             this.el.on("touchstart", this.onTouchStart, this);
18536             this.el.on("touchmove", this.onTouchMove, this);
18537             this.el.on("touchend", this.onTouchEnd, this);
18538         }
18539         
18540     },
18541     
18542     onRender : function(ct, position)
18543     {
18544         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18545     },
18546     
18547     setActive : function(state)
18548     {
18549         Roo.log("panel - set active " + this.tabId + "=" + state);
18550         
18551         this.active = state;
18552         if (!state) {
18553             this.el.removeClass('active');
18554             
18555         } else  if (!this.el.hasClass('active')) {
18556             this.el.addClass('active');
18557         }
18558         
18559         this.fireEvent('changed', this, state);
18560     },
18561     
18562     onClick : function(e)
18563     {
18564         e.preventDefault();
18565         
18566         if(!this.href.length){
18567             return;
18568         }
18569         
18570         window.location.href = this.href;
18571     },
18572     
18573     startX : 0,
18574     startY : 0,
18575     endX : 0,
18576     endY : 0,
18577     swiping : false,
18578     
18579     onTouchStart : function(e)
18580     {
18581         this.swiping = false;
18582         
18583         this.startX = e.browserEvent.touches[0].clientX;
18584         this.startY = e.browserEvent.touches[0].clientY;
18585     },
18586     
18587     onTouchMove : function(e)
18588     {
18589         this.swiping = true;
18590         
18591         this.endX = e.browserEvent.touches[0].clientX;
18592         this.endY = e.browserEvent.touches[0].clientY;
18593     },
18594     
18595     onTouchEnd : function(e)
18596     {
18597         if(!this.swiping){
18598             this.onClick(e);
18599             return;
18600         }
18601         
18602         var tabGroup = this.parent();
18603         
18604         if(this.endX > this.startX){ // swiping right
18605             tabGroup.showPanelPrev();
18606             return;
18607         }
18608         
18609         if(this.startX > this.endX){ // swiping left
18610             tabGroup.showPanelNext();
18611             return;
18612         }
18613     }
18614     
18615     
18616 });
18617  
18618
18619  
18620
18621  /*
18622  * - LGPL
18623  *
18624  * DateField
18625  * 
18626  */
18627
18628 /**
18629  * @class Roo.bootstrap.DateField
18630  * @extends Roo.bootstrap.Input
18631  * Bootstrap DateField class
18632  * @cfg {Number} weekStart default 0
18633  * @cfg {String} viewMode default empty, (months|years)
18634  * @cfg {String} minViewMode default empty, (months|years)
18635  * @cfg {Number} startDate default -Infinity
18636  * @cfg {Number} endDate default Infinity
18637  * @cfg {Boolean} todayHighlight default false
18638  * @cfg {Boolean} todayBtn default false
18639  * @cfg {Boolean} calendarWeeks default false
18640  * @cfg {Object} daysOfWeekDisabled default empty
18641  * @cfg {Boolean} singleMode default false (true | false)
18642  * 
18643  * @cfg {Boolean} keyboardNavigation default true
18644  * @cfg {String} language default en
18645  * 
18646  * @constructor
18647  * Create a new DateField
18648  * @param {Object} config The config object
18649  */
18650
18651 Roo.bootstrap.DateField = function(config){
18652     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18653      this.addEvents({
18654             /**
18655              * @event show
18656              * Fires when this field show.
18657              * @param {Roo.bootstrap.DateField} this
18658              * @param {Mixed} date The date value
18659              */
18660             show : true,
18661             /**
18662              * @event show
18663              * Fires when this field hide.
18664              * @param {Roo.bootstrap.DateField} this
18665              * @param {Mixed} date The date value
18666              */
18667             hide : true,
18668             /**
18669              * @event select
18670              * Fires when select a date.
18671              * @param {Roo.bootstrap.DateField} this
18672              * @param {Mixed} date The date value
18673              */
18674             select : true,
18675             /**
18676              * @event beforeselect
18677              * Fires when before select a date.
18678              * @param {Roo.bootstrap.DateField} this
18679              * @param {Mixed} date The date value
18680              */
18681             beforeselect : true
18682         });
18683 };
18684
18685 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18686     
18687     /**
18688      * @cfg {String} format
18689      * The default date format string which can be overriden for localization support.  The format must be
18690      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18691      */
18692     format : "m/d/y",
18693     /**
18694      * @cfg {String} altFormats
18695      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18696      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18697      */
18698     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18699     
18700     weekStart : 0,
18701     
18702     viewMode : '',
18703     
18704     minViewMode : '',
18705     
18706     todayHighlight : false,
18707     
18708     todayBtn: false,
18709     
18710     language: 'en',
18711     
18712     keyboardNavigation: true,
18713     
18714     calendarWeeks: false,
18715     
18716     startDate: -Infinity,
18717     
18718     endDate: Infinity,
18719     
18720     daysOfWeekDisabled: [],
18721     
18722     _events: [],
18723     
18724     singleMode : false,
18725     
18726     UTCDate: function()
18727     {
18728         return new Date(Date.UTC.apply(Date, arguments));
18729     },
18730     
18731     UTCToday: function()
18732     {
18733         var today = new Date();
18734         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18735     },
18736     
18737     getDate: function() {
18738             var d = this.getUTCDate();
18739             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18740     },
18741     
18742     getUTCDate: function() {
18743             return this.date;
18744     },
18745     
18746     setDate: function(d) {
18747             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18748     },
18749     
18750     setUTCDate: function(d) {
18751             this.date = d;
18752             this.setValue(this.formatDate(this.date));
18753     },
18754         
18755     onRender: function(ct, position)
18756     {
18757         
18758         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18759         
18760         this.language = this.language || 'en';
18761         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18762         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18763         
18764         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18765         this.format = this.format || 'm/d/y';
18766         this.isInline = false;
18767         this.isInput = true;
18768         this.component = this.el.select('.add-on', true).first() || false;
18769         this.component = (this.component && this.component.length === 0) ? false : this.component;
18770         this.hasInput = this.component && this.inputEl().length;
18771         
18772         if (typeof(this.minViewMode === 'string')) {
18773             switch (this.minViewMode) {
18774                 case 'months':
18775                     this.minViewMode = 1;
18776                     break;
18777                 case 'years':
18778                     this.minViewMode = 2;
18779                     break;
18780                 default:
18781                     this.minViewMode = 0;
18782                     break;
18783             }
18784         }
18785         
18786         if (typeof(this.viewMode === 'string')) {
18787             switch (this.viewMode) {
18788                 case 'months':
18789                     this.viewMode = 1;
18790                     break;
18791                 case 'years':
18792                     this.viewMode = 2;
18793                     break;
18794                 default:
18795                     this.viewMode = 0;
18796                     break;
18797             }
18798         }
18799                 
18800         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18801         
18802 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18803         
18804         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18805         
18806         this.picker().on('mousedown', this.onMousedown, this);
18807         this.picker().on('click', this.onClick, this);
18808         
18809         this.picker().addClass('datepicker-dropdown');
18810         
18811         this.startViewMode = this.viewMode;
18812         
18813         if(this.singleMode){
18814             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18815                 v.setVisibilityMode(Roo.Element.DISPLAY);
18816                 v.hide();
18817             });
18818             
18819             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18820                 v.setStyle('width', '189px');
18821             });
18822         }
18823         
18824         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18825             if(!this.calendarWeeks){
18826                 v.remove();
18827                 return;
18828             }
18829             
18830             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18831             v.attr('colspan', function(i, val){
18832                 return parseInt(val) + 1;
18833             });
18834         });
18835                         
18836         
18837         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18838         
18839         this.setStartDate(this.startDate);
18840         this.setEndDate(this.endDate);
18841         
18842         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18843         
18844         this.fillDow();
18845         this.fillMonths();
18846         this.update();
18847         this.showMode();
18848         
18849         if(this.isInline) {
18850             this.showPopup();
18851         }
18852     },
18853     
18854     picker : function()
18855     {
18856         return this.pickerEl;
18857 //        return this.el.select('.datepicker', true).first();
18858     },
18859     
18860     fillDow: function()
18861     {
18862         var dowCnt = this.weekStart;
18863         
18864         var dow = {
18865             tag: 'tr',
18866             cn: [
18867                 
18868             ]
18869         };
18870         
18871         if(this.calendarWeeks){
18872             dow.cn.push({
18873                 tag: 'th',
18874                 cls: 'cw',
18875                 html: '&nbsp;'
18876             })
18877         }
18878         
18879         while (dowCnt < this.weekStart + 7) {
18880             dow.cn.push({
18881                 tag: 'th',
18882                 cls: 'dow',
18883                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18884             });
18885         }
18886         
18887         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18888     },
18889     
18890     fillMonths: function()
18891     {    
18892         var i = 0;
18893         var months = this.picker().select('>.datepicker-months td', true).first();
18894         
18895         months.dom.innerHTML = '';
18896         
18897         while (i < 12) {
18898             var month = {
18899                 tag: 'span',
18900                 cls: 'month',
18901                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18902             };
18903             
18904             months.createChild(month);
18905         }
18906         
18907     },
18908     
18909     update: function()
18910     {
18911         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
18912         
18913         if (this.date < this.startDate) {
18914             this.viewDate = new Date(this.startDate);
18915         } else if (this.date > this.endDate) {
18916             this.viewDate = new Date(this.endDate);
18917         } else {
18918             this.viewDate = new Date(this.date);
18919         }
18920         
18921         this.fill();
18922     },
18923     
18924     fill: function() 
18925     {
18926         var d = new Date(this.viewDate),
18927                 year = d.getUTCFullYear(),
18928                 month = d.getUTCMonth(),
18929                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18930                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18931                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18932                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18933                 currentDate = this.date && this.date.valueOf(),
18934                 today = this.UTCToday();
18935         
18936         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18937         
18938 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18939         
18940 //        this.picker.select('>tfoot th.today').
18941 //                                              .text(dates[this.language].today)
18942 //                                              .toggle(this.todayBtn !== false);
18943     
18944         this.updateNavArrows();
18945         this.fillMonths();
18946                                                 
18947         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18948         
18949         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18950          
18951         prevMonth.setUTCDate(day);
18952         
18953         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18954         
18955         var nextMonth = new Date(prevMonth);
18956         
18957         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18958         
18959         nextMonth = nextMonth.valueOf();
18960         
18961         var fillMonths = false;
18962         
18963         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18964         
18965         while(prevMonth.valueOf() <= nextMonth) {
18966             var clsName = '';
18967             
18968             if (prevMonth.getUTCDay() === this.weekStart) {
18969                 if(fillMonths){
18970                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18971                 }
18972                     
18973                 fillMonths = {
18974                     tag: 'tr',
18975                     cn: []
18976                 };
18977                 
18978                 if(this.calendarWeeks){
18979                     // ISO 8601: First week contains first thursday.
18980                     // ISO also states week starts on Monday, but we can be more abstract here.
18981                     var
18982                     // Start of current week: based on weekstart/current date
18983                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18984                     // Thursday of this week
18985                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18986                     // First Thursday of year, year from thursday
18987                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18988                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18989                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18990                     
18991                     fillMonths.cn.push({
18992                         tag: 'td',
18993                         cls: 'cw',
18994                         html: calWeek
18995                     });
18996                 }
18997             }
18998             
18999             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19000                 clsName += ' old';
19001             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19002                 clsName += ' new';
19003             }
19004             if (this.todayHighlight &&
19005                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19006                 prevMonth.getUTCMonth() == today.getMonth() &&
19007                 prevMonth.getUTCDate() == today.getDate()) {
19008                 clsName += ' today';
19009             }
19010             
19011             if (currentDate && prevMonth.valueOf() === currentDate) {
19012                 clsName += ' active';
19013             }
19014             
19015             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19016                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19017                     clsName += ' disabled';
19018             }
19019             
19020             fillMonths.cn.push({
19021                 tag: 'td',
19022                 cls: 'day ' + clsName,
19023                 html: prevMonth.getDate()
19024             });
19025             
19026             prevMonth.setDate(prevMonth.getDate()+1);
19027         }
19028           
19029         var currentYear = this.date && this.date.getUTCFullYear();
19030         var currentMonth = this.date && this.date.getUTCMonth();
19031         
19032         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19033         
19034         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19035             v.removeClass('active');
19036             
19037             if(currentYear === year && k === currentMonth){
19038                 v.addClass('active');
19039             }
19040             
19041             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19042                 v.addClass('disabled');
19043             }
19044             
19045         });
19046         
19047         
19048         year = parseInt(year/10, 10) * 10;
19049         
19050         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19051         
19052         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19053         
19054         year -= 1;
19055         for (var i = -1; i < 11; i++) {
19056             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19057                 tag: 'span',
19058                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19059                 html: year
19060             });
19061             
19062             year += 1;
19063         }
19064     },
19065     
19066     showMode: function(dir) 
19067     {
19068         if (dir) {
19069             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19070         }
19071         
19072         Roo.each(this.picker().select('>div',true).elements, function(v){
19073             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19074             v.hide();
19075         });
19076         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19077     },
19078     
19079     place: function()
19080     {
19081         if(this.isInline) {
19082             return;
19083         }
19084         
19085         this.picker().removeClass(['bottom', 'top']);
19086         
19087         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19088             /*
19089              * place to the top of element!
19090              *
19091              */
19092             
19093             this.picker().addClass('top');
19094             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19095             
19096             return;
19097         }
19098         
19099         this.picker().addClass('bottom');
19100         
19101         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19102     },
19103     
19104     parseDate : function(value)
19105     {
19106         if(!value || value instanceof Date){
19107             return value;
19108         }
19109         var v = Date.parseDate(value, this.format);
19110         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19111             v = Date.parseDate(value, 'Y-m-d');
19112         }
19113         if(!v && this.altFormats){
19114             if(!this.altFormatsArray){
19115                 this.altFormatsArray = this.altFormats.split("|");
19116             }
19117             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19118                 v = Date.parseDate(value, this.altFormatsArray[i]);
19119             }
19120         }
19121         return v;
19122     },
19123     
19124     formatDate : function(date, fmt)
19125     {   
19126         return (!date || !(date instanceof Date)) ?
19127         date : date.dateFormat(fmt || this.format);
19128     },
19129     
19130     onFocus : function()
19131     {
19132         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19133         this.showPopup();
19134     },
19135     
19136     onBlur : function()
19137     {
19138         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19139         
19140         var d = this.inputEl().getValue();
19141         
19142         this.setValue(d);
19143                 
19144         this.hidePopup();
19145     },
19146     
19147     showPopup : function()
19148     {
19149         this.picker().show();
19150         this.update();
19151         this.place();
19152         
19153         this.fireEvent('showpopup', this, this.date);
19154     },
19155     
19156     hidePopup : function()
19157     {
19158         if(this.isInline) {
19159             return;
19160         }
19161         this.picker().hide();
19162         this.viewMode = this.startViewMode;
19163         this.showMode();
19164         
19165         this.fireEvent('hidepopup', this, this.date);
19166         
19167     },
19168     
19169     onMousedown: function(e)
19170     {
19171         e.stopPropagation();
19172         e.preventDefault();
19173     },
19174     
19175     keyup: function(e)
19176     {
19177         Roo.bootstrap.DateField.superclass.keyup.call(this);
19178         this.update();
19179     },
19180
19181     setValue: function(v)
19182     {
19183         if(this.fireEvent('beforeselect', this, v) !== false){
19184             var d = new Date(this.parseDate(v) ).clearTime();
19185         
19186             if(isNaN(d.getTime())){
19187                 this.date = this.viewDate = '';
19188                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19189                 return;
19190             }
19191
19192             v = this.formatDate(d);
19193
19194             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19195
19196             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19197
19198             this.update();
19199
19200             this.fireEvent('select', this, this.date);
19201         }
19202     },
19203     
19204     getValue: function()
19205     {
19206         return this.formatDate(this.date);
19207     },
19208     
19209     fireKey: function(e)
19210     {
19211         if (!this.picker().isVisible()){
19212             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19213                 this.showPopup();
19214             }
19215             return;
19216         }
19217         
19218         var dateChanged = false,
19219         dir, day, month,
19220         newDate, newViewDate;
19221         
19222         switch(e.keyCode){
19223             case 27: // escape
19224                 this.hidePopup();
19225                 e.preventDefault();
19226                 break;
19227             case 37: // left
19228             case 39: // right
19229                 if (!this.keyboardNavigation) {
19230                     break;
19231                 }
19232                 dir = e.keyCode == 37 ? -1 : 1;
19233                 
19234                 if (e.ctrlKey){
19235                     newDate = this.moveYear(this.date, dir);
19236                     newViewDate = this.moveYear(this.viewDate, dir);
19237                 } else if (e.shiftKey){
19238                     newDate = this.moveMonth(this.date, dir);
19239                     newViewDate = this.moveMonth(this.viewDate, dir);
19240                 } else {
19241                     newDate = new Date(this.date);
19242                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19243                     newViewDate = new Date(this.viewDate);
19244                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19245                 }
19246                 if (this.dateWithinRange(newDate)){
19247                     this.date = newDate;
19248                     this.viewDate = newViewDate;
19249                     this.setValue(this.formatDate(this.date));
19250 //                    this.update();
19251                     e.preventDefault();
19252                     dateChanged = true;
19253                 }
19254                 break;
19255             case 38: // up
19256             case 40: // down
19257                 if (!this.keyboardNavigation) {
19258                     break;
19259                 }
19260                 dir = e.keyCode == 38 ? -1 : 1;
19261                 if (e.ctrlKey){
19262                     newDate = this.moveYear(this.date, dir);
19263                     newViewDate = this.moveYear(this.viewDate, dir);
19264                 } else if (e.shiftKey){
19265                     newDate = this.moveMonth(this.date, dir);
19266                     newViewDate = this.moveMonth(this.viewDate, dir);
19267                 } else {
19268                     newDate = new Date(this.date);
19269                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19270                     newViewDate = new Date(this.viewDate);
19271                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19272                 }
19273                 if (this.dateWithinRange(newDate)){
19274                     this.date = newDate;
19275                     this.viewDate = newViewDate;
19276                     this.setValue(this.formatDate(this.date));
19277 //                    this.update();
19278                     e.preventDefault();
19279                     dateChanged = true;
19280                 }
19281                 break;
19282             case 13: // enter
19283                 this.setValue(this.formatDate(this.date));
19284                 this.hidePopup();
19285                 e.preventDefault();
19286                 break;
19287             case 9: // tab
19288                 this.setValue(this.formatDate(this.date));
19289                 this.hidePopup();
19290                 break;
19291             case 16: // shift
19292             case 17: // ctrl
19293             case 18: // alt
19294                 break;
19295             default :
19296                 this.hidePopup();
19297                 
19298         }
19299     },
19300     
19301     
19302     onClick: function(e) 
19303     {
19304         e.stopPropagation();
19305         e.preventDefault();
19306         
19307         var target = e.getTarget();
19308         
19309         if(target.nodeName.toLowerCase() === 'i'){
19310             target = Roo.get(target).dom.parentNode;
19311         }
19312         
19313         var nodeName = target.nodeName;
19314         var className = target.className;
19315         var html = target.innerHTML;
19316         //Roo.log(nodeName);
19317         
19318         switch(nodeName.toLowerCase()) {
19319             case 'th':
19320                 switch(className) {
19321                     case 'switch':
19322                         this.showMode(1);
19323                         break;
19324                     case 'prev':
19325                     case 'next':
19326                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19327                         switch(this.viewMode){
19328                                 case 0:
19329                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19330                                         break;
19331                                 case 1:
19332                                 case 2:
19333                                         this.viewDate = this.moveYear(this.viewDate, dir);
19334                                         break;
19335                         }
19336                         this.fill();
19337                         break;
19338                     case 'today':
19339                         var date = new Date();
19340                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19341 //                        this.fill()
19342                         this.setValue(this.formatDate(this.date));
19343                         
19344                         this.hidePopup();
19345                         break;
19346                 }
19347                 break;
19348             case 'span':
19349                 if (className.indexOf('disabled') < 0) {
19350                     this.viewDate.setUTCDate(1);
19351                     if (className.indexOf('month') > -1) {
19352                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19353                     } else {
19354                         var year = parseInt(html, 10) || 0;
19355                         this.viewDate.setUTCFullYear(year);
19356                         
19357                     }
19358                     
19359                     if(this.singleMode){
19360                         this.setValue(this.formatDate(this.viewDate));
19361                         this.hidePopup();
19362                         return;
19363                     }
19364                     
19365                     this.showMode(-1);
19366                     this.fill();
19367                 }
19368                 break;
19369                 
19370             case 'td':
19371                 //Roo.log(className);
19372                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19373                     var day = parseInt(html, 10) || 1;
19374                     var year = this.viewDate.getUTCFullYear(),
19375                         month = this.viewDate.getUTCMonth();
19376
19377                     if (className.indexOf('old') > -1) {
19378                         if(month === 0 ){
19379                             month = 11;
19380                             year -= 1;
19381                         }else{
19382                             month -= 1;
19383                         }
19384                     } else if (className.indexOf('new') > -1) {
19385                         if (month == 11) {
19386                             month = 0;
19387                             year += 1;
19388                         } else {
19389                             month += 1;
19390                         }
19391                     }
19392                     //Roo.log([year,month,day]);
19393                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19394                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19395 //                    this.fill();
19396                     //Roo.log(this.formatDate(this.date));
19397                     this.setValue(this.formatDate(this.date));
19398                     this.hidePopup();
19399                 }
19400                 break;
19401         }
19402     },
19403     
19404     setStartDate: function(startDate)
19405     {
19406         this.startDate = startDate || -Infinity;
19407         if (this.startDate !== -Infinity) {
19408             this.startDate = this.parseDate(this.startDate);
19409         }
19410         this.update();
19411         this.updateNavArrows();
19412     },
19413
19414     setEndDate: function(endDate)
19415     {
19416         this.endDate = endDate || Infinity;
19417         if (this.endDate !== Infinity) {
19418             this.endDate = this.parseDate(this.endDate);
19419         }
19420         this.update();
19421         this.updateNavArrows();
19422     },
19423     
19424     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19425     {
19426         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19427         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19428             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19429         }
19430         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19431             return parseInt(d, 10);
19432         });
19433         this.update();
19434         this.updateNavArrows();
19435     },
19436     
19437     updateNavArrows: function() 
19438     {
19439         if(this.singleMode){
19440             return;
19441         }
19442         
19443         var d = new Date(this.viewDate),
19444         year = d.getUTCFullYear(),
19445         month = d.getUTCMonth();
19446         
19447         Roo.each(this.picker().select('.prev', true).elements, function(v){
19448             v.show();
19449             switch (this.viewMode) {
19450                 case 0:
19451
19452                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19453                         v.hide();
19454                     }
19455                     break;
19456                 case 1:
19457                 case 2:
19458                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19459                         v.hide();
19460                     }
19461                     break;
19462             }
19463         });
19464         
19465         Roo.each(this.picker().select('.next', true).elements, function(v){
19466             v.show();
19467             switch (this.viewMode) {
19468                 case 0:
19469
19470                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19471                         v.hide();
19472                     }
19473                     break;
19474                 case 1:
19475                 case 2:
19476                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19477                         v.hide();
19478                     }
19479                     break;
19480             }
19481         })
19482     },
19483     
19484     moveMonth: function(date, dir)
19485     {
19486         if (!dir) {
19487             return date;
19488         }
19489         var new_date = new Date(date.valueOf()),
19490         day = new_date.getUTCDate(),
19491         month = new_date.getUTCMonth(),
19492         mag = Math.abs(dir),
19493         new_month, test;
19494         dir = dir > 0 ? 1 : -1;
19495         if (mag == 1){
19496             test = dir == -1
19497             // If going back one month, make sure month is not current month
19498             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19499             ? function(){
19500                 return new_date.getUTCMonth() == month;
19501             }
19502             // If going forward one month, make sure month is as expected
19503             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19504             : function(){
19505                 return new_date.getUTCMonth() != new_month;
19506             };
19507             new_month = month + dir;
19508             new_date.setUTCMonth(new_month);
19509             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19510             if (new_month < 0 || new_month > 11) {
19511                 new_month = (new_month + 12) % 12;
19512             }
19513         } else {
19514             // For magnitudes >1, move one month at a time...
19515             for (var i=0; i<mag; i++) {
19516                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19517                 new_date = this.moveMonth(new_date, dir);
19518             }
19519             // ...then reset the day, keeping it in the new month
19520             new_month = new_date.getUTCMonth();
19521             new_date.setUTCDate(day);
19522             test = function(){
19523                 return new_month != new_date.getUTCMonth();
19524             };
19525         }
19526         // Common date-resetting loop -- if date is beyond end of month, make it
19527         // end of month
19528         while (test()){
19529             new_date.setUTCDate(--day);
19530             new_date.setUTCMonth(new_month);
19531         }
19532         return new_date;
19533     },
19534
19535     moveYear: function(date, dir)
19536     {
19537         return this.moveMonth(date, dir*12);
19538     },
19539
19540     dateWithinRange: function(date)
19541     {
19542         return date >= this.startDate && date <= this.endDate;
19543     },
19544
19545     
19546     remove: function() 
19547     {
19548         this.picker().remove();
19549     },
19550     
19551     validateValue : function(value)
19552     {
19553         if(this.getVisibilityEl().hasClass('hidden')){
19554             return true;
19555         }
19556         
19557         if(value.length < 1)  {
19558             if(this.allowBlank){
19559                 return true;
19560             }
19561             return false;
19562         }
19563         
19564         if(value.length < this.minLength){
19565             return false;
19566         }
19567         if(value.length > this.maxLength){
19568             return false;
19569         }
19570         if(this.vtype){
19571             var vt = Roo.form.VTypes;
19572             if(!vt[this.vtype](value, this)){
19573                 return false;
19574             }
19575         }
19576         if(typeof this.validator == "function"){
19577             var msg = this.validator(value);
19578             if(msg !== true){
19579                 return false;
19580             }
19581         }
19582         
19583         if(this.regex && !this.regex.test(value)){
19584             return false;
19585         }
19586         
19587         if(typeof(this.parseDate(value)) == 'undefined'){
19588             return false;
19589         }
19590         
19591         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19592             return false;
19593         }      
19594         
19595         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19596             return false;
19597         } 
19598         
19599         
19600         return true;
19601     },
19602     
19603     reset : function()
19604     {
19605         this.date = this.viewDate = '';
19606         
19607         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19608     }
19609    
19610 });
19611
19612 Roo.apply(Roo.bootstrap.DateField,  {
19613     
19614     head : {
19615         tag: 'thead',
19616         cn: [
19617         {
19618             tag: 'tr',
19619             cn: [
19620             {
19621                 tag: 'th',
19622                 cls: 'prev',
19623                 html: '<i class="fa fa-arrow-left"/>'
19624             },
19625             {
19626                 tag: 'th',
19627                 cls: 'switch',
19628                 colspan: '5'
19629             },
19630             {
19631                 tag: 'th',
19632                 cls: 'next',
19633                 html: '<i class="fa fa-arrow-right"/>'
19634             }
19635
19636             ]
19637         }
19638         ]
19639     },
19640     
19641     content : {
19642         tag: 'tbody',
19643         cn: [
19644         {
19645             tag: 'tr',
19646             cn: [
19647             {
19648                 tag: 'td',
19649                 colspan: '7'
19650             }
19651             ]
19652         }
19653         ]
19654     },
19655     
19656     footer : {
19657         tag: 'tfoot',
19658         cn: [
19659         {
19660             tag: 'tr',
19661             cn: [
19662             {
19663                 tag: 'th',
19664                 colspan: '7',
19665                 cls: 'today'
19666             }
19667                     
19668             ]
19669         }
19670         ]
19671     },
19672     
19673     dates:{
19674         en: {
19675             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19676             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19677             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19678             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19679             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19680             today: "Today"
19681         }
19682     },
19683     
19684     modes: [
19685     {
19686         clsName: 'days',
19687         navFnc: 'Month',
19688         navStep: 1
19689     },
19690     {
19691         clsName: 'months',
19692         navFnc: 'FullYear',
19693         navStep: 1
19694     },
19695     {
19696         clsName: 'years',
19697         navFnc: 'FullYear',
19698         navStep: 10
19699     }]
19700 });
19701
19702 Roo.apply(Roo.bootstrap.DateField,  {
19703   
19704     template : {
19705         tag: 'div',
19706         cls: 'datepicker dropdown-menu roo-dynamic',
19707         cn: [
19708         {
19709             tag: 'div',
19710             cls: 'datepicker-days',
19711             cn: [
19712             {
19713                 tag: 'table',
19714                 cls: 'table-condensed',
19715                 cn:[
19716                 Roo.bootstrap.DateField.head,
19717                 {
19718                     tag: 'tbody'
19719                 },
19720                 Roo.bootstrap.DateField.footer
19721                 ]
19722             }
19723             ]
19724         },
19725         {
19726             tag: 'div',
19727             cls: 'datepicker-months',
19728             cn: [
19729             {
19730                 tag: 'table',
19731                 cls: 'table-condensed',
19732                 cn:[
19733                 Roo.bootstrap.DateField.head,
19734                 Roo.bootstrap.DateField.content,
19735                 Roo.bootstrap.DateField.footer
19736                 ]
19737             }
19738             ]
19739         },
19740         {
19741             tag: 'div',
19742             cls: 'datepicker-years',
19743             cn: [
19744             {
19745                 tag: 'table',
19746                 cls: 'table-condensed',
19747                 cn:[
19748                 Roo.bootstrap.DateField.head,
19749                 Roo.bootstrap.DateField.content,
19750                 Roo.bootstrap.DateField.footer
19751                 ]
19752             }
19753             ]
19754         }
19755         ]
19756     }
19757 });
19758
19759  
19760
19761  /*
19762  * - LGPL
19763  *
19764  * TimeField
19765  * 
19766  */
19767
19768 /**
19769  * @class Roo.bootstrap.TimeField
19770  * @extends Roo.bootstrap.Input
19771  * Bootstrap DateField class
19772  * 
19773  * 
19774  * @constructor
19775  * Create a new TimeField
19776  * @param {Object} config The config object
19777  */
19778
19779 Roo.bootstrap.TimeField = function(config){
19780     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19781     this.addEvents({
19782             /**
19783              * @event show
19784              * Fires when this field show.
19785              * @param {Roo.bootstrap.DateField} thisthis
19786              * @param {Mixed} date The date value
19787              */
19788             show : true,
19789             /**
19790              * @event show
19791              * Fires when this field hide.
19792              * @param {Roo.bootstrap.DateField} this
19793              * @param {Mixed} date The date value
19794              */
19795             hide : true,
19796             /**
19797              * @event select
19798              * Fires when select a date.
19799              * @param {Roo.bootstrap.DateField} this
19800              * @param {Mixed} date The date value
19801              */
19802             select : true
19803         });
19804 };
19805
19806 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19807     
19808     /**
19809      * @cfg {String} format
19810      * The default time format string which can be overriden for localization support.  The format must be
19811      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19812      */
19813     format : "H:i",
19814        
19815     onRender: function(ct, position)
19816     {
19817         
19818         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19819                 
19820         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19821         
19822         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19823         
19824         this.pop = this.picker().select('>.datepicker-time',true).first();
19825         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19826         
19827         this.picker().on('mousedown', this.onMousedown, this);
19828         this.picker().on('click', this.onClick, this);
19829         
19830         this.picker().addClass('datepicker-dropdown');
19831     
19832         this.fillTime();
19833         this.update();
19834             
19835         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19836         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19837         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19838         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19839         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19840         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19841
19842     },
19843     
19844     fireKey: function(e){
19845         if (!this.picker().isVisible()){
19846             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19847                 this.show();
19848             }
19849             return;
19850         }
19851
19852         e.preventDefault();
19853         
19854         switch(e.keyCode){
19855             case 27: // escape
19856                 this.hide();
19857                 break;
19858             case 37: // left
19859             case 39: // right
19860                 this.onTogglePeriod();
19861                 break;
19862             case 38: // up
19863                 this.onIncrementMinutes();
19864                 break;
19865             case 40: // down
19866                 this.onDecrementMinutes();
19867                 break;
19868             case 13: // enter
19869             case 9: // tab
19870                 this.setTime();
19871                 break;
19872         }
19873     },
19874     
19875     onClick: function(e) {
19876         e.stopPropagation();
19877         e.preventDefault();
19878     },
19879     
19880     picker : function()
19881     {
19882         return this.el.select('.datepicker', true).first();
19883     },
19884     
19885     fillTime: function()
19886     {    
19887         var time = this.pop.select('tbody', true).first();
19888         
19889         time.dom.innerHTML = '';
19890         
19891         time.createChild({
19892             tag: 'tr',
19893             cn: [
19894                 {
19895                     tag: 'td',
19896                     cn: [
19897                         {
19898                             tag: 'a',
19899                             href: '#',
19900                             cls: 'btn',
19901                             cn: [
19902                                 {
19903                                     tag: 'span',
19904                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19905                                 }
19906                             ]
19907                         } 
19908                     ]
19909                 },
19910                 {
19911                     tag: 'td',
19912                     cls: 'separator'
19913                 },
19914                 {
19915                     tag: 'td',
19916                     cn: [
19917                         {
19918                             tag: 'a',
19919                             href: '#',
19920                             cls: 'btn',
19921                             cn: [
19922                                 {
19923                                     tag: 'span',
19924                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19925                                 }
19926                             ]
19927                         }
19928                     ]
19929                 },
19930                 {
19931                     tag: 'td',
19932                     cls: 'separator'
19933                 }
19934             ]
19935         });
19936         
19937         time.createChild({
19938             tag: 'tr',
19939             cn: [
19940                 {
19941                     tag: 'td',
19942                     cn: [
19943                         {
19944                             tag: 'span',
19945                             cls: 'timepicker-hour',
19946                             html: '00'
19947                         }  
19948                     ]
19949                 },
19950                 {
19951                     tag: 'td',
19952                     cls: 'separator',
19953                     html: ':'
19954                 },
19955                 {
19956                     tag: 'td',
19957                     cn: [
19958                         {
19959                             tag: 'span',
19960                             cls: 'timepicker-minute',
19961                             html: '00'
19962                         }  
19963                     ]
19964                 },
19965                 {
19966                     tag: 'td',
19967                     cls: 'separator'
19968                 },
19969                 {
19970                     tag: 'td',
19971                     cn: [
19972                         {
19973                             tag: 'button',
19974                             type: 'button',
19975                             cls: 'btn btn-primary period',
19976                             html: 'AM'
19977                             
19978                         }
19979                     ]
19980                 }
19981             ]
19982         });
19983         
19984         time.createChild({
19985             tag: 'tr',
19986             cn: [
19987                 {
19988                     tag: 'td',
19989                     cn: [
19990                         {
19991                             tag: 'a',
19992                             href: '#',
19993                             cls: 'btn',
19994                             cn: [
19995                                 {
19996                                     tag: 'span',
19997                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19998                                 }
19999                             ]
20000                         }
20001                     ]
20002                 },
20003                 {
20004                     tag: 'td',
20005                     cls: 'separator'
20006                 },
20007                 {
20008                     tag: 'td',
20009                     cn: [
20010                         {
20011                             tag: 'a',
20012                             href: '#',
20013                             cls: 'btn',
20014                             cn: [
20015                                 {
20016                                     tag: 'span',
20017                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20018                                 }
20019                             ]
20020                         }
20021                     ]
20022                 },
20023                 {
20024                     tag: 'td',
20025                     cls: 'separator'
20026                 }
20027             ]
20028         });
20029         
20030     },
20031     
20032     update: function()
20033     {
20034         
20035         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20036         
20037         this.fill();
20038     },
20039     
20040     fill: function() 
20041     {
20042         var hours = this.time.getHours();
20043         var minutes = this.time.getMinutes();
20044         var period = 'AM';
20045         
20046         if(hours > 11){
20047             period = 'PM';
20048         }
20049         
20050         if(hours == 0){
20051             hours = 12;
20052         }
20053         
20054         
20055         if(hours > 12){
20056             hours = hours - 12;
20057         }
20058         
20059         if(hours < 10){
20060             hours = '0' + hours;
20061         }
20062         
20063         if(minutes < 10){
20064             minutes = '0' + minutes;
20065         }
20066         
20067         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20068         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20069         this.pop.select('button', true).first().dom.innerHTML = period;
20070         
20071     },
20072     
20073     place: function()
20074     {   
20075         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20076         
20077         var cls = ['bottom'];
20078         
20079         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20080             cls.pop();
20081             cls.push('top');
20082         }
20083         
20084         cls.push('right');
20085         
20086         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20087             cls.pop();
20088             cls.push('left');
20089         }
20090         
20091         this.picker().addClass(cls.join('-'));
20092         
20093         var _this = this;
20094         
20095         Roo.each(cls, function(c){
20096             if(c == 'bottom'){
20097                 _this.picker().setTop(_this.inputEl().getHeight());
20098                 return;
20099             }
20100             if(c == 'top'){
20101                 _this.picker().setTop(0 - _this.picker().getHeight());
20102                 return;
20103             }
20104             
20105             if(c == 'left'){
20106                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20107                 return;
20108             }
20109             if(c == 'right'){
20110                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20111                 return;
20112             }
20113         });
20114         
20115     },
20116   
20117     onFocus : function()
20118     {
20119         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20120         this.show();
20121     },
20122     
20123     onBlur : function()
20124     {
20125         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20126         this.hide();
20127     },
20128     
20129     show : function()
20130     {
20131         this.picker().show();
20132         this.pop.show();
20133         this.update();
20134         this.place();
20135         
20136         this.fireEvent('show', this, this.date);
20137     },
20138     
20139     hide : function()
20140     {
20141         this.picker().hide();
20142         this.pop.hide();
20143         
20144         this.fireEvent('hide', this, this.date);
20145     },
20146     
20147     setTime : function()
20148     {
20149         this.hide();
20150         this.setValue(this.time.format(this.format));
20151         
20152         this.fireEvent('select', this, this.date);
20153         
20154         
20155     },
20156     
20157     onMousedown: function(e){
20158         e.stopPropagation();
20159         e.preventDefault();
20160     },
20161     
20162     onIncrementHours: function()
20163     {
20164         Roo.log('onIncrementHours');
20165         this.time = this.time.add(Date.HOUR, 1);
20166         this.update();
20167         
20168     },
20169     
20170     onDecrementHours: function()
20171     {
20172         Roo.log('onDecrementHours');
20173         this.time = this.time.add(Date.HOUR, -1);
20174         this.update();
20175     },
20176     
20177     onIncrementMinutes: function()
20178     {
20179         Roo.log('onIncrementMinutes');
20180         this.time = this.time.add(Date.MINUTE, 1);
20181         this.update();
20182     },
20183     
20184     onDecrementMinutes: function()
20185     {
20186         Roo.log('onDecrementMinutes');
20187         this.time = this.time.add(Date.MINUTE, -1);
20188         this.update();
20189     },
20190     
20191     onTogglePeriod: function()
20192     {
20193         Roo.log('onTogglePeriod');
20194         this.time = this.time.add(Date.HOUR, 12);
20195         this.update();
20196     }
20197     
20198    
20199 });
20200
20201 Roo.apply(Roo.bootstrap.TimeField,  {
20202     
20203     content : {
20204         tag: 'tbody',
20205         cn: [
20206             {
20207                 tag: 'tr',
20208                 cn: [
20209                 {
20210                     tag: 'td',
20211                     colspan: '7'
20212                 }
20213                 ]
20214             }
20215         ]
20216     },
20217     
20218     footer : {
20219         tag: 'tfoot',
20220         cn: [
20221             {
20222                 tag: 'tr',
20223                 cn: [
20224                 {
20225                     tag: 'th',
20226                     colspan: '7',
20227                     cls: '',
20228                     cn: [
20229                         {
20230                             tag: 'button',
20231                             cls: 'btn btn-info ok',
20232                             html: 'OK'
20233                         }
20234                     ]
20235                 }
20236
20237                 ]
20238             }
20239         ]
20240     }
20241 });
20242
20243 Roo.apply(Roo.bootstrap.TimeField,  {
20244   
20245     template : {
20246         tag: 'div',
20247         cls: 'datepicker dropdown-menu',
20248         cn: [
20249             {
20250                 tag: 'div',
20251                 cls: 'datepicker-time',
20252                 cn: [
20253                 {
20254                     tag: 'table',
20255                     cls: 'table-condensed',
20256                     cn:[
20257                     Roo.bootstrap.TimeField.content,
20258                     Roo.bootstrap.TimeField.footer
20259                     ]
20260                 }
20261                 ]
20262             }
20263         ]
20264     }
20265 });
20266
20267  
20268
20269  /*
20270  * - LGPL
20271  *
20272  * MonthField
20273  * 
20274  */
20275
20276 /**
20277  * @class Roo.bootstrap.MonthField
20278  * @extends Roo.bootstrap.Input
20279  * Bootstrap MonthField class
20280  * 
20281  * @cfg {String} language default en
20282  * 
20283  * @constructor
20284  * Create a new MonthField
20285  * @param {Object} config The config object
20286  */
20287
20288 Roo.bootstrap.MonthField = function(config){
20289     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20290     
20291     this.addEvents({
20292         /**
20293          * @event show
20294          * Fires when this field show.
20295          * @param {Roo.bootstrap.MonthField} this
20296          * @param {Mixed} date The date value
20297          */
20298         show : true,
20299         /**
20300          * @event show
20301          * Fires when this field hide.
20302          * @param {Roo.bootstrap.MonthField} this
20303          * @param {Mixed} date The date value
20304          */
20305         hide : true,
20306         /**
20307          * @event select
20308          * Fires when select a date.
20309          * @param {Roo.bootstrap.MonthField} this
20310          * @param {String} oldvalue The old value
20311          * @param {String} newvalue The new value
20312          */
20313         select : true
20314     });
20315 };
20316
20317 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20318     
20319     onRender: function(ct, position)
20320     {
20321         
20322         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20323         
20324         this.language = this.language || 'en';
20325         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20326         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20327         
20328         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20329         this.isInline = false;
20330         this.isInput = true;
20331         this.component = this.el.select('.add-on', true).first() || false;
20332         this.component = (this.component && this.component.length === 0) ? false : this.component;
20333         this.hasInput = this.component && this.inputEL().length;
20334         
20335         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20336         
20337         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20338         
20339         this.picker().on('mousedown', this.onMousedown, this);
20340         this.picker().on('click', this.onClick, this);
20341         
20342         this.picker().addClass('datepicker-dropdown');
20343         
20344         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20345             v.setStyle('width', '189px');
20346         });
20347         
20348         this.fillMonths();
20349         
20350         this.update();
20351         
20352         if(this.isInline) {
20353             this.show();
20354         }
20355         
20356     },
20357     
20358     setValue: function(v, suppressEvent)
20359     {   
20360         var o = this.getValue();
20361         
20362         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20363         
20364         this.update();
20365
20366         if(suppressEvent !== true){
20367             this.fireEvent('select', this, o, v);
20368         }
20369         
20370     },
20371     
20372     getValue: function()
20373     {
20374         return this.value;
20375     },
20376     
20377     onClick: function(e) 
20378     {
20379         e.stopPropagation();
20380         e.preventDefault();
20381         
20382         var target = e.getTarget();
20383         
20384         if(target.nodeName.toLowerCase() === 'i'){
20385             target = Roo.get(target).dom.parentNode;
20386         }
20387         
20388         var nodeName = target.nodeName;
20389         var className = target.className;
20390         var html = target.innerHTML;
20391         
20392         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20393             return;
20394         }
20395         
20396         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20397         
20398         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20399         
20400         this.hide();
20401                         
20402     },
20403     
20404     picker : function()
20405     {
20406         return this.pickerEl;
20407     },
20408     
20409     fillMonths: function()
20410     {    
20411         var i = 0;
20412         var months = this.picker().select('>.datepicker-months td', true).first();
20413         
20414         months.dom.innerHTML = '';
20415         
20416         while (i < 12) {
20417             var month = {
20418                 tag: 'span',
20419                 cls: 'month',
20420                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20421             };
20422             
20423             months.createChild(month);
20424         }
20425         
20426     },
20427     
20428     update: function()
20429     {
20430         var _this = this;
20431         
20432         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20433             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20434         }
20435         
20436         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20437             e.removeClass('active');
20438             
20439             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20440                 e.addClass('active');
20441             }
20442         })
20443     },
20444     
20445     place: function()
20446     {
20447         if(this.isInline) {
20448             return;
20449         }
20450         
20451         this.picker().removeClass(['bottom', 'top']);
20452         
20453         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20454             /*
20455              * place to the top of element!
20456              *
20457              */
20458             
20459             this.picker().addClass('top');
20460             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20461             
20462             return;
20463         }
20464         
20465         this.picker().addClass('bottom');
20466         
20467         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20468     },
20469     
20470     onFocus : function()
20471     {
20472         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20473         this.show();
20474     },
20475     
20476     onBlur : function()
20477     {
20478         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20479         
20480         var d = this.inputEl().getValue();
20481         
20482         this.setValue(d);
20483                 
20484         this.hide();
20485     },
20486     
20487     show : function()
20488     {
20489         this.picker().show();
20490         this.picker().select('>.datepicker-months', true).first().show();
20491         this.update();
20492         this.place();
20493         
20494         this.fireEvent('show', this, this.date);
20495     },
20496     
20497     hide : function()
20498     {
20499         if(this.isInline) {
20500             return;
20501         }
20502         this.picker().hide();
20503         this.fireEvent('hide', this, this.date);
20504         
20505     },
20506     
20507     onMousedown: function(e)
20508     {
20509         e.stopPropagation();
20510         e.preventDefault();
20511     },
20512     
20513     keyup: function(e)
20514     {
20515         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20516         this.update();
20517     },
20518
20519     fireKey: function(e)
20520     {
20521         if (!this.picker().isVisible()){
20522             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20523                 this.show();
20524             }
20525             return;
20526         }
20527         
20528         var dir;
20529         
20530         switch(e.keyCode){
20531             case 27: // escape
20532                 this.hide();
20533                 e.preventDefault();
20534                 break;
20535             case 37: // left
20536             case 39: // right
20537                 dir = e.keyCode == 37 ? -1 : 1;
20538                 
20539                 this.vIndex = this.vIndex + dir;
20540                 
20541                 if(this.vIndex < 0){
20542                     this.vIndex = 0;
20543                 }
20544                 
20545                 if(this.vIndex > 11){
20546                     this.vIndex = 11;
20547                 }
20548                 
20549                 if(isNaN(this.vIndex)){
20550                     this.vIndex = 0;
20551                 }
20552                 
20553                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20554                 
20555                 break;
20556             case 38: // up
20557             case 40: // down
20558                 
20559                 dir = e.keyCode == 38 ? -1 : 1;
20560                 
20561                 this.vIndex = this.vIndex + dir * 4;
20562                 
20563                 if(this.vIndex < 0){
20564                     this.vIndex = 0;
20565                 }
20566                 
20567                 if(this.vIndex > 11){
20568                     this.vIndex = 11;
20569                 }
20570                 
20571                 if(isNaN(this.vIndex)){
20572                     this.vIndex = 0;
20573                 }
20574                 
20575                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20576                 break;
20577                 
20578             case 13: // enter
20579                 
20580                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20581                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20582                 }
20583                 
20584                 this.hide();
20585                 e.preventDefault();
20586                 break;
20587             case 9: // tab
20588                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20589                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20590                 }
20591                 this.hide();
20592                 break;
20593             case 16: // shift
20594             case 17: // ctrl
20595             case 18: // alt
20596                 break;
20597             default :
20598                 this.hide();
20599                 
20600         }
20601     },
20602     
20603     remove: function() 
20604     {
20605         this.picker().remove();
20606     }
20607    
20608 });
20609
20610 Roo.apply(Roo.bootstrap.MonthField,  {
20611     
20612     content : {
20613         tag: 'tbody',
20614         cn: [
20615         {
20616             tag: 'tr',
20617             cn: [
20618             {
20619                 tag: 'td',
20620                 colspan: '7'
20621             }
20622             ]
20623         }
20624         ]
20625     },
20626     
20627     dates:{
20628         en: {
20629             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20630             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20631         }
20632     }
20633 });
20634
20635 Roo.apply(Roo.bootstrap.MonthField,  {
20636   
20637     template : {
20638         tag: 'div',
20639         cls: 'datepicker dropdown-menu roo-dynamic',
20640         cn: [
20641             {
20642                 tag: 'div',
20643                 cls: 'datepicker-months',
20644                 cn: [
20645                 {
20646                     tag: 'table',
20647                     cls: 'table-condensed',
20648                     cn:[
20649                         Roo.bootstrap.DateField.content
20650                     ]
20651                 }
20652                 ]
20653             }
20654         ]
20655     }
20656 });
20657
20658  
20659
20660  
20661  /*
20662  * - LGPL
20663  *
20664  * CheckBox
20665  * 
20666  */
20667
20668 /**
20669  * @class Roo.bootstrap.CheckBox
20670  * @extends Roo.bootstrap.Input
20671  * Bootstrap CheckBox class
20672  * 
20673  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20674  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20675  * @cfg {String} boxLabel The text that appears beside the checkbox
20676  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20677  * @cfg {Boolean} checked initnal the element
20678  * @cfg {Boolean} inline inline the element (default false)
20679  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20680  * @cfg {String} tooltip label tooltip
20681  * 
20682  * @constructor
20683  * Create a new CheckBox
20684  * @param {Object} config The config object
20685  */
20686
20687 Roo.bootstrap.CheckBox = function(config){
20688     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20689    
20690     this.addEvents({
20691         /**
20692         * @event check
20693         * Fires when the element is checked or unchecked.
20694         * @param {Roo.bootstrap.CheckBox} this This input
20695         * @param {Boolean} checked The new checked value
20696         */
20697        check : true,
20698        /**
20699         * @event click
20700         * Fires when the element is click.
20701         * @param {Roo.bootstrap.CheckBox} this This input
20702         */
20703        click : true
20704     });
20705     
20706 };
20707
20708 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20709   
20710     inputType: 'checkbox',
20711     inputValue: 1,
20712     valueOff: 0,
20713     boxLabel: false,
20714     checked: false,
20715     weight : false,
20716     inline: false,
20717     tooltip : '',
20718     
20719     getAutoCreate : function()
20720     {
20721         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20722         
20723         var id = Roo.id();
20724         
20725         var cfg = {};
20726         
20727         cfg.cls = 'form-group ' + this.inputType; //input-group
20728         
20729         if(this.inline){
20730             cfg.cls += ' ' + this.inputType + '-inline';
20731         }
20732         
20733         var input =  {
20734             tag: 'input',
20735             id : id,
20736             type : this.inputType,
20737             value : this.inputValue,
20738             cls : 'roo-' + this.inputType, //'form-box',
20739             placeholder : this.placeholder || ''
20740             
20741         };
20742         
20743         if(this.inputType != 'radio'){
20744             var hidden =  {
20745                 tag: 'input',
20746                 type : 'hidden',
20747                 cls : 'roo-hidden-value',
20748                 value : this.checked ? this.inputValue : this.valueOff
20749             };
20750         }
20751         
20752             
20753         if (this.weight) { // Validity check?
20754             cfg.cls += " " + this.inputType + "-" + this.weight;
20755         }
20756         
20757         if (this.disabled) {
20758             input.disabled=true;
20759         }
20760         
20761         if(this.checked){
20762             input.checked = this.checked;
20763         }
20764         
20765         if (this.name) {
20766             
20767             input.name = this.name;
20768             
20769             if(this.inputType != 'radio'){
20770                 hidden.name = this.name;
20771                 input.name = '_hidden_' + this.name;
20772             }
20773         }
20774         
20775         if (this.size) {
20776             input.cls += ' input-' + this.size;
20777         }
20778         
20779         var settings=this;
20780         
20781         ['xs','sm','md','lg'].map(function(size){
20782             if (settings[size]) {
20783                 cfg.cls += ' col-' + size + '-' + settings[size];
20784             }
20785         });
20786         
20787         var inputblock = input;
20788          
20789         if (this.before || this.after) {
20790             
20791             inputblock = {
20792                 cls : 'input-group',
20793                 cn :  [] 
20794             };
20795             
20796             if (this.before) {
20797                 inputblock.cn.push({
20798                     tag :'span',
20799                     cls : 'input-group-addon',
20800                     html : this.before
20801                 });
20802             }
20803             
20804             inputblock.cn.push(input);
20805             
20806             if(this.inputType != 'radio'){
20807                 inputblock.cn.push(hidden);
20808             }
20809             
20810             if (this.after) {
20811                 inputblock.cn.push({
20812                     tag :'span',
20813                     cls : 'input-group-addon',
20814                     html : this.after
20815                 });
20816             }
20817             
20818         }
20819         
20820         if (align ==='left' && this.fieldLabel.length) {
20821 //                Roo.log("left and has label");
20822             cfg.cn = [
20823                 {
20824                     tag: 'label',
20825                     'for' :  id,
20826                     cls : 'control-label',
20827                     html : this.fieldLabel
20828                 },
20829                 {
20830                     cls : "", 
20831                     cn: [
20832                         inputblock
20833                     ]
20834                 }
20835             ];
20836             
20837             if(this.labelWidth > 12){
20838                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20839             }
20840             
20841             if(this.labelWidth < 13 && this.labelmd == 0){
20842                 this.labelmd = this.labelWidth;
20843             }
20844             
20845             if(this.labellg > 0){
20846                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20847                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20848             }
20849             
20850             if(this.labelmd > 0){
20851                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20852                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20853             }
20854             
20855             if(this.labelsm > 0){
20856                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20857                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20858             }
20859             
20860             if(this.labelxs > 0){
20861                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20862                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20863             }
20864             
20865         } else if ( this.fieldLabel.length) {
20866 //                Roo.log(" label");
20867                 cfg.cn = [
20868                    
20869                     {
20870                         tag: this.boxLabel ? 'span' : 'label',
20871                         'for': id,
20872                         cls: 'control-label box-input-label',
20873                         //cls : 'input-group-addon',
20874                         html : this.fieldLabel
20875                     },
20876                     
20877                     inputblock
20878                     
20879                 ];
20880
20881         } else {
20882             
20883 //                Roo.log(" no label && no align");
20884                 cfg.cn = [  inputblock ] ;
20885                 
20886                 
20887         }
20888         
20889         if(this.boxLabel){
20890              var boxLabelCfg = {
20891                 tag: 'label',
20892                 //'for': id, // box label is handled by onclick - so no for...
20893                 cls: 'box-label',
20894                 html: this.boxLabel
20895             };
20896             
20897             if(this.tooltip){
20898                 boxLabelCfg.tooltip = this.tooltip;
20899             }
20900              
20901             cfg.cn.push(boxLabelCfg);
20902         }
20903         
20904         if(this.inputType != 'radio'){
20905             cfg.cn.push(hidden);
20906         }
20907         
20908         return cfg;
20909         
20910     },
20911     
20912     /**
20913      * return the real input element.
20914      */
20915     inputEl: function ()
20916     {
20917         return this.el.select('input.roo-' + this.inputType,true).first();
20918     },
20919     hiddenEl: function ()
20920     {
20921         return this.el.select('input.roo-hidden-value',true).first();
20922     },
20923     
20924     labelEl: function()
20925     {
20926         return this.el.select('label.control-label',true).first();
20927     },
20928     /* depricated... */
20929     
20930     label: function()
20931     {
20932         return this.labelEl();
20933     },
20934     
20935     boxLabelEl: function()
20936     {
20937         return this.el.select('label.box-label',true).first();
20938     },
20939     
20940     initEvents : function()
20941     {
20942 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20943         
20944         this.inputEl().on('click', this.onClick,  this);
20945         
20946         if (this.boxLabel) { 
20947             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20948         }
20949         
20950         this.startValue = this.getValue();
20951         
20952         if(this.groupId){
20953             Roo.bootstrap.CheckBox.register(this);
20954         }
20955     },
20956     
20957     onClick : function(e)
20958     {   
20959         if(this.fireEvent('click', this, e) !== false){
20960             this.setChecked(!this.checked);
20961         }
20962         
20963     },
20964     
20965     setChecked : function(state,suppressEvent)
20966     {
20967         this.startValue = this.getValue();
20968
20969         if(this.inputType == 'radio'){
20970             
20971             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20972                 e.dom.checked = false;
20973             });
20974             
20975             this.inputEl().dom.checked = true;
20976             
20977             this.inputEl().dom.value = this.inputValue;
20978             
20979             if(suppressEvent !== true){
20980                 this.fireEvent('check', this, true);
20981             }
20982             
20983             this.validate();
20984             
20985             return;
20986         }
20987         
20988         this.checked = state;
20989         
20990         this.inputEl().dom.checked = state;
20991         
20992         
20993         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20994         
20995         if(suppressEvent !== true){
20996             this.fireEvent('check', this, state);
20997         }
20998         
20999         this.validate();
21000     },
21001     
21002     getValue : function()
21003     {
21004         if(this.inputType == 'radio'){
21005             return this.getGroupValue();
21006         }
21007         
21008         return this.hiddenEl().dom.value;
21009         
21010     },
21011     
21012     getGroupValue : function()
21013     {
21014         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21015             return '';
21016         }
21017         
21018         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21019     },
21020     
21021     setValue : function(v,suppressEvent)
21022     {
21023         if(this.inputType == 'radio'){
21024             this.setGroupValue(v, suppressEvent);
21025             return;
21026         }
21027         
21028         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21029         
21030         this.validate();
21031     },
21032     
21033     setGroupValue : function(v, suppressEvent)
21034     {
21035         this.startValue = this.getValue();
21036         
21037         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21038             e.dom.checked = false;
21039             
21040             if(e.dom.value == v){
21041                 e.dom.checked = true;
21042             }
21043         });
21044         
21045         if(suppressEvent !== true){
21046             this.fireEvent('check', this, true);
21047         }
21048
21049         this.validate();
21050         
21051         return;
21052     },
21053     
21054     validate : function()
21055     {
21056         if(this.getVisibilityEl().hasClass('hidden')){
21057             return true;
21058         }
21059         
21060         if(
21061                 this.disabled || 
21062                 (this.inputType == 'radio' && this.validateRadio()) ||
21063                 (this.inputType == 'checkbox' && this.validateCheckbox())
21064         ){
21065             this.markValid();
21066             return true;
21067         }
21068         
21069         this.markInvalid();
21070         return false;
21071     },
21072     
21073     validateRadio : function()
21074     {
21075         if(this.getVisibilityEl().hasClass('hidden')){
21076             return true;
21077         }
21078         
21079         if(this.allowBlank){
21080             return true;
21081         }
21082         
21083         var valid = false;
21084         
21085         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21086             if(!e.dom.checked){
21087                 return;
21088             }
21089             
21090             valid = true;
21091             
21092             return false;
21093         });
21094         
21095         return valid;
21096     },
21097     
21098     validateCheckbox : function()
21099     {
21100         if(!this.groupId){
21101             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21102             //return (this.getValue() == this.inputValue) ? true : false;
21103         }
21104         
21105         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21106         
21107         if(!group){
21108             return false;
21109         }
21110         
21111         var r = false;
21112         
21113         for(var i in group){
21114             if(group[i].el.isVisible(true)){
21115                 r = false;
21116                 break;
21117             }
21118             
21119             r = true;
21120         }
21121         
21122         for(var i in group){
21123             if(r){
21124                 break;
21125             }
21126             
21127             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21128         }
21129         
21130         return r;
21131     },
21132     
21133     /**
21134      * Mark this field as valid
21135      */
21136     markValid : function()
21137     {
21138         var _this = this;
21139         
21140         this.fireEvent('valid', this);
21141         
21142         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21143         
21144         if(this.groupId){
21145             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21146         }
21147         
21148         if(label){
21149             label.markValid();
21150         }
21151
21152         if(this.inputType == 'radio'){
21153             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21154                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21155                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21156             });
21157             
21158             return;
21159         }
21160
21161         if(!this.groupId){
21162             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21163             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21164             return;
21165         }
21166         
21167         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21168         
21169         if(!group){
21170             return;
21171         }
21172         
21173         for(var i in group){
21174             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21175             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21176         }
21177     },
21178     
21179      /**
21180      * Mark this field as invalid
21181      * @param {String} msg The validation message
21182      */
21183     markInvalid : function(msg)
21184     {
21185         if(this.allowBlank){
21186             return;
21187         }
21188         
21189         var _this = this;
21190         
21191         this.fireEvent('invalid', this, msg);
21192         
21193         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21194         
21195         if(this.groupId){
21196             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21197         }
21198         
21199         if(label){
21200             label.markInvalid();
21201         }
21202             
21203         if(this.inputType == 'radio'){
21204             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21205                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21206                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21207             });
21208             
21209             return;
21210         }
21211         
21212         if(!this.groupId){
21213             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21214             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21215             return;
21216         }
21217         
21218         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21219         
21220         if(!group){
21221             return;
21222         }
21223         
21224         for(var i in group){
21225             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21226             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21227         }
21228         
21229     },
21230     
21231     clearInvalid : function()
21232     {
21233         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21234         
21235         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21236         
21237         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21238         
21239         if (label && label.iconEl) {
21240             label.iconEl.removeClass(label.validClass);
21241             label.iconEl.removeClass(label.invalidClass);
21242         }
21243     },
21244     
21245     disable : function()
21246     {
21247         if(this.inputType != 'radio'){
21248             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21249             return;
21250         }
21251         
21252         var _this = this;
21253         
21254         if(this.rendered){
21255             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21256                 _this.getActionEl().addClass(this.disabledClass);
21257                 e.dom.disabled = true;
21258             });
21259         }
21260         
21261         this.disabled = true;
21262         this.fireEvent("disable", this);
21263         return this;
21264     },
21265
21266     enable : function()
21267     {
21268         if(this.inputType != 'radio'){
21269             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21270             return;
21271         }
21272         
21273         var _this = this;
21274         
21275         if(this.rendered){
21276             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21277                 _this.getActionEl().removeClass(this.disabledClass);
21278                 e.dom.disabled = false;
21279             });
21280         }
21281         
21282         this.disabled = false;
21283         this.fireEvent("enable", this);
21284         return this;
21285     },
21286     
21287     setBoxLabel : function(v)
21288     {
21289         this.boxLabel = v;
21290         
21291         if(this.rendered){
21292             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21293         }
21294     }
21295
21296 });
21297
21298 Roo.apply(Roo.bootstrap.CheckBox, {
21299     
21300     groups: {},
21301     
21302      /**
21303     * register a CheckBox Group
21304     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21305     */
21306     register : function(checkbox)
21307     {
21308         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21309             this.groups[checkbox.groupId] = {};
21310         }
21311         
21312         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21313             return;
21314         }
21315         
21316         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21317         
21318     },
21319     /**
21320     * fetch a CheckBox Group based on the group ID
21321     * @param {string} the group ID
21322     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21323     */
21324     get: function(groupId) {
21325         if (typeof(this.groups[groupId]) == 'undefined') {
21326             return false;
21327         }
21328         
21329         return this.groups[groupId] ;
21330     }
21331     
21332     
21333 });
21334 /*
21335  * - LGPL
21336  *
21337  * RadioItem
21338  * 
21339  */
21340
21341 /**
21342  * @class Roo.bootstrap.Radio
21343  * @extends Roo.bootstrap.Component
21344  * Bootstrap Radio class
21345  * @cfg {String} boxLabel - the label associated
21346  * @cfg {String} value - the value of radio
21347  * 
21348  * @constructor
21349  * Create a new Radio
21350  * @param {Object} config The config object
21351  */
21352 Roo.bootstrap.Radio = function(config){
21353     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21354     
21355 };
21356
21357 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21358     
21359     boxLabel : '',
21360     
21361     value : '',
21362     
21363     getAutoCreate : function()
21364     {
21365         var cfg = {
21366             tag : 'div',
21367             cls : 'form-group radio',
21368             cn : [
21369                 {
21370                     tag : 'label',
21371                     cls : 'box-label',
21372                     html : this.boxLabel
21373                 }
21374             ]
21375         };
21376         
21377         return cfg;
21378     },
21379     
21380     initEvents : function() 
21381     {
21382         this.parent().register(this);
21383         
21384         this.el.on('click', this.onClick, this);
21385         
21386     },
21387     
21388     onClick : function(e)
21389     {
21390         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21391             this.setChecked(true);
21392         }
21393     },
21394     
21395     setChecked : function(state, suppressEvent)
21396     {
21397         this.parent().setValue(this.value, suppressEvent);
21398         
21399     },
21400     
21401     setBoxLabel : function(v)
21402     {
21403         this.boxLabel = v;
21404         
21405         if(this.rendered){
21406             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21407         }
21408     }
21409     
21410 });
21411  
21412
21413  /*
21414  * - LGPL
21415  *
21416  * Input
21417  * 
21418  */
21419
21420 /**
21421  * @class Roo.bootstrap.SecurePass
21422  * @extends Roo.bootstrap.Input
21423  * Bootstrap SecurePass class
21424  *
21425  * 
21426  * @constructor
21427  * Create a new SecurePass
21428  * @param {Object} config The config object
21429  */
21430  
21431 Roo.bootstrap.SecurePass = function (config) {
21432     // these go here, so the translation tool can replace them..
21433     this.errors = {
21434         PwdEmpty: "Please type a password, and then retype it to confirm.",
21435         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21436         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21437         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21438         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21439         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21440         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21441         TooWeak: "Your password is Too Weak."
21442     },
21443     this.meterLabel = "Password strength:";
21444     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21445     this.meterClass = [
21446         "roo-password-meter-tooweak", 
21447         "roo-password-meter-weak", 
21448         "roo-password-meter-medium", 
21449         "roo-password-meter-strong", 
21450         "roo-password-meter-grey"
21451     ];
21452     
21453     this.errors = {};
21454     
21455     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21456 }
21457
21458 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21459     /**
21460      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21461      * {
21462      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21463      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21464      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21465      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21466      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21467      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21468      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21469      * })
21470      */
21471     // private
21472     
21473     meterWidth: 300,
21474     errorMsg :'',    
21475     errors: false,
21476     imageRoot: '/',
21477     /**
21478      * @cfg {String/Object} Label for the strength meter (defaults to
21479      * 'Password strength:')
21480      */
21481     // private
21482     meterLabel: '',
21483     /**
21484      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21485      * ['Weak', 'Medium', 'Strong'])
21486      */
21487     // private    
21488     pwdStrengths: false,    
21489     // private
21490     strength: 0,
21491     // private
21492     _lastPwd: null,
21493     // private
21494     kCapitalLetter: 0,
21495     kSmallLetter: 1,
21496     kDigit: 2,
21497     kPunctuation: 3,
21498     
21499     insecure: false,
21500     // private
21501     initEvents: function ()
21502     {
21503         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21504
21505         if (this.el.is('input[type=password]') && Roo.isSafari) {
21506             this.el.on('keydown', this.SafariOnKeyDown, this);
21507         }
21508
21509         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21510     },
21511     // private
21512     onRender: function (ct, position)
21513     {
21514         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21515         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21516         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21517
21518         this.trigger.createChild({
21519                    cn: [
21520                     {
21521                     //id: 'PwdMeter',
21522                     tag: 'div',
21523                     cls: 'roo-password-meter-grey col-xs-12',
21524                     style: {
21525                         //width: 0,
21526                         //width: this.meterWidth + 'px'                                                
21527                         }
21528                     },
21529                     {                            
21530                          cls: 'roo-password-meter-text'                          
21531                     }
21532                 ]            
21533         });
21534
21535          
21536         if (this.hideTrigger) {
21537             this.trigger.setDisplayed(false);
21538         }
21539         this.setSize(this.width || '', this.height || '');
21540     },
21541     // private
21542     onDestroy: function ()
21543     {
21544         if (this.trigger) {
21545             this.trigger.removeAllListeners();
21546             this.trigger.remove();
21547         }
21548         if (this.wrap) {
21549             this.wrap.remove();
21550         }
21551         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21552     },
21553     // private
21554     checkStrength: function ()
21555     {
21556         var pwd = this.inputEl().getValue();
21557         if (pwd == this._lastPwd) {
21558             return;
21559         }
21560
21561         var strength;
21562         if (this.ClientSideStrongPassword(pwd)) {
21563             strength = 3;
21564         } else if (this.ClientSideMediumPassword(pwd)) {
21565             strength = 2;
21566         } else if (this.ClientSideWeakPassword(pwd)) {
21567             strength = 1;
21568         } else {
21569             strength = 0;
21570         }
21571         
21572         Roo.log('strength1: ' + strength);
21573         
21574         //var pm = this.trigger.child('div/div/div').dom;
21575         var pm = this.trigger.child('div/div');
21576         pm.removeClass(this.meterClass);
21577         pm.addClass(this.meterClass[strength]);
21578                 
21579         
21580         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21581                 
21582         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21583         
21584         this._lastPwd = pwd;
21585     },
21586     reset: function ()
21587     {
21588         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21589         
21590         this._lastPwd = '';
21591         
21592         var pm = this.trigger.child('div/div');
21593         pm.removeClass(this.meterClass);
21594         pm.addClass('roo-password-meter-grey');        
21595         
21596         
21597         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21598         
21599         pt.innerHTML = '';
21600         this.inputEl().dom.type='password';
21601     },
21602     // private
21603     validateValue: function (value)
21604     {
21605         
21606         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21607             return false;
21608         }
21609         if (value.length == 0) {
21610             if (this.allowBlank) {
21611                 this.clearInvalid();
21612                 return true;
21613             }
21614
21615             this.markInvalid(this.errors.PwdEmpty);
21616             this.errorMsg = this.errors.PwdEmpty;
21617             return false;
21618         }
21619         
21620         if(this.insecure){
21621             return true;
21622         }
21623         
21624         if ('[\x21-\x7e]*'.match(value)) {
21625             this.markInvalid(this.errors.PwdBadChar);
21626             this.errorMsg = this.errors.PwdBadChar;
21627             return false;
21628         }
21629         if (value.length < 6) {
21630             this.markInvalid(this.errors.PwdShort);
21631             this.errorMsg = this.errors.PwdShort;
21632             return false;
21633         }
21634         if (value.length > 16) {
21635             this.markInvalid(this.errors.PwdLong);
21636             this.errorMsg = this.errors.PwdLong;
21637             return false;
21638         }
21639         var strength;
21640         if (this.ClientSideStrongPassword(value)) {
21641             strength = 3;
21642         } else if (this.ClientSideMediumPassword(value)) {
21643             strength = 2;
21644         } else if (this.ClientSideWeakPassword(value)) {
21645             strength = 1;
21646         } else {
21647             strength = 0;
21648         }
21649
21650         
21651         if (strength < 2) {
21652             //this.markInvalid(this.errors.TooWeak);
21653             this.errorMsg = this.errors.TooWeak;
21654             //return false;
21655         }
21656         
21657         
21658         console.log('strength2: ' + strength);
21659         
21660         //var pm = this.trigger.child('div/div/div').dom;
21661         
21662         var pm = this.trigger.child('div/div');
21663         pm.removeClass(this.meterClass);
21664         pm.addClass(this.meterClass[strength]);
21665                 
21666         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21667                 
21668         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21669         
21670         this.errorMsg = ''; 
21671         return true;
21672     },
21673     // private
21674     CharacterSetChecks: function (type)
21675     {
21676         this.type = type;
21677         this.fResult = false;
21678     },
21679     // private
21680     isctype: function (character, type)
21681     {
21682         switch (type) {  
21683             case this.kCapitalLetter:
21684                 if (character >= 'A' && character <= 'Z') {
21685                     return true;
21686                 }
21687                 break;
21688             
21689             case this.kSmallLetter:
21690                 if (character >= 'a' && character <= 'z') {
21691                     return true;
21692                 }
21693                 break;
21694             
21695             case this.kDigit:
21696                 if (character >= '0' && character <= '9') {
21697                     return true;
21698                 }
21699                 break;
21700             
21701             case this.kPunctuation:
21702                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21703                     return true;
21704                 }
21705                 break;
21706             
21707             default:
21708                 return false;
21709         }
21710
21711     },
21712     // private
21713     IsLongEnough: function (pwd, size)
21714     {
21715         return !(pwd == null || isNaN(size) || pwd.length < size);
21716     },
21717     // private
21718     SpansEnoughCharacterSets: function (word, nb)
21719     {
21720         if (!this.IsLongEnough(word, nb))
21721         {
21722             return false;
21723         }
21724
21725         var characterSetChecks = new Array(
21726             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21727             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21728         );
21729         
21730         for (var index = 0; index < word.length; ++index) {
21731             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21732                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21733                     characterSetChecks[nCharSet].fResult = true;
21734                     break;
21735                 }
21736             }
21737         }
21738
21739         var nCharSets = 0;
21740         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21741             if (characterSetChecks[nCharSet].fResult) {
21742                 ++nCharSets;
21743             }
21744         }
21745
21746         if (nCharSets < nb) {
21747             return false;
21748         }
21749         return true;
21750     },
21751     // private
21752     ClientSideStrongPassword: function (pwd)
21753     {
21754         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21755     },
21756     // private
21757     ClientSideMediumPassword: function (pwd)
21758     {
21759         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21760     },
21761     // private
21762     ClientSideWeakPassword: function (pwd)
21763     {
21764         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21765     }
21766           
21767 })//<script type="text/javascript">
21768
21769 /*
21770  * Based  Ext JS Library 1.1.1
21771  * Copyright(c) 2006-2007, Ext JS, LLC.
21772  * LGPL
21773  *
21774  */
21775  
21776 /**
21777  * @class Roo.HtmlEditorCore
21778  * @extends Roo.Component
21779  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21780  *
21781  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21782  */
21783
21784 Roo.HtmlEditorCore = function(config){
21785     
21786     
21787     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21788     
21789     
21790     this.addEvents({
21791         /**
21792          * @event initialize
21793          * Fires when the editor is fully initialized (including the iframe)
21794          * @param {Roo.HtmlEditorCore} this
21795          */
21796         initialize: true,
21797         /**
21798          * @event activate
21799          * Fires when the editor is first receives the focus. Any insertion must wait
21800          * until after this event.
21801          * @param {Roo.HtmlEditorCore} this
21802          */
21803         activate: true,
21804          /**
21805          * @event beforesync
21806          * Fires before the textarea is updated with content from the editor iframe. Return false
21807          * to cancel the sync.
21808          * @param {Roo.HtmlEditorCore} this
21809          * @param {String} html
21810          */
21811         beforesync: true,
21812          /**
21813          * @event beforepush
21814          * Fires before the iframe editor is updated with content from the textarea. Return false
21815          * to cancel the push.
21816          * @param {Roo.HtmlEditorCore} this
21817          * @param {String} html
21818          */
21819         beforepush: true,
21820          /**
21821          * @event sync
21822          * Fires when the textarea is updated with content from the editor iframe.
21823          * @param {Roo.HtmlEditorCore} this
21824          * @param {String} html
21825          */
21826         sync: true,
21827          /**
21828          * @event push
21829          * Fires when the iframe editor is updated with content from the textarea.
21830          * @param {Roo.HtmlEditorCore} this
21831          * @param {String} html
21832          */
21833         push: true,
21834         
21835         /**
21836          * @event editorevent
21837          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21838          * @param {Roo.HtmlEditorCore} this
21839          */
21840         editorevent: true
21841         
21842     });
21843     
21844     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21845     
21846     // defaults : white / black...
21847     this.applyBlacklists();
21848     
21849     
21850     
21851 };
21852
21853
21854 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21855
21856
21857      /**
21858      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21859      */
21860     
21861     owner : false,
21862     
21863      /**
21864      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21865      *                        Roo.resizable.
21866      */
21867     resizable : false,
21868      /**
21869      * @cfg {Number} height (in pixels)
21870      */   
21871     height: 300,
21872    /**
21873      * @cfg {Number} width (in pixels)
21874      */   
21875     width: 500,
21876     
21877     /**
21878      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21879      * 
21880      */
21881     stylesheets: false,
21882     
21883     // id of frame..
21884     frameId: false,
21885     
21886     // private properties
21887     validationEvent : false,
21888     deferHeight: true,
21889     initialized : false,
21890     activated : false,
21891     sourceEditMode : false,
21892     onFocus : Roo.emptyFn,
21893     iframePad:3,
21894     hideMode:'offsets',
21895     
21896     clearUp: true,
21897     
21898     // blacklist + whitelisted elements..
21899     black: false,
21900     white: false,
21901      
21902     bodyCls : '',
21903
21904     /**
21905      * Protected method that will not generally be called directly. It
21906      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21907      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21908      */
21909     getDocMarkup : function(){
21910         // body styles..
21911         var st = '';
21912         
21913         // inherit styels from page...?? 
21914         if (this.stylesheets === false) {
21915             
21916             Roo.get(document.head).select('style').each(function(node) {
21917                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21918             });
21919             
21920             Roo.get(document.head).select('link').each(function(node) { 
21921                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21922             });
21923             
21924         } else if (!this.stylesheets.length) {
21925                 // simple..
21926                 st = '<style type="text/css">' +
21927                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21928                    '</style>';
21929         } else { 
21930             st = '<style type="text/css">' +
21931                     this.stylesheets +
21932                 '</style>';
21933         }
21934         
21935         st +=  '<style type="text/css">' +
21936             'IMG { cursor: pointer } ' +
21937         '</style>';
21938
21939         var cls = 'roo-htmleditor-body';
21940         
21941         if(this.bodyCls.length){
21942             cls += ' ' + this.bodyCls;
21943         }
21944         
21945         return '<html><head>' + st  +
21946             //<style type="text/css">' +
21947             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21948             //'</style>' +
21949             ' </head><body class="' +  cls + '"></body></html>';
21950     },
21951
21952     // private
21953     onRender : function(ct, position)
21954     {
21955         var _t = this;
21956         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21957         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21958         
21959         
21960         this.el.dom.style.border = '0 none';
21961         this.el.dom.setAttribute('tabIndex', -1);
21962         this.el.addClass('x-hidden hide');
21963         
21964         
21965         
21966         if(Roo.isIE){ // fix IE 1px bogus margin
21967             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21968         }
21969        
21970         
21971         this.frameId = Roo.id();
21972         
21973          
21974         
21975         var iframe = this.owner.wrap.createChild({
21976             tag: 'iframe',
21977             cls: 'form-control', // bootstrap..
21978             id: this.frameId,
21979             name: this.frameId,
21980             frameBorder : 'no',
21981             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21982         }, this.el
21983         );
21984         
21985         
21986         this.iframe = iframe.dom;
21987
21988          this.assignDocWin();
21989         
21990         this.doc.designMode = 'on';
21991        
21992         this.doc.open();
21993         this.doc.write(this.getDocMarkup());
21994         this.doc.close();
21995
21996         
21997         var task = { // must defer to wait for browser to be ready
21998             run : function(){
21999                 //console.log("run task?" + this.doc.readyState);
22000                 this.assignDocWin();
22001                 if(this.doc.body || this.doc.readyState == 'complete'){
22002                     try {
22003                         this.doc.designMode="on";
22004                     } catch (e) {
22005                         return;
22006                     }
22007                     Roo.TaskMgr.stop(task);
22008                     this.initEditor.defer(10, this);
22009                 }
22010             },
22011             interval : 10,
22012             duration: 10000,
22013             scope: this
22014         };
22015         Roo.TaskMgr.start(task);
22016
22017     },
22018
22019     // private
22020     onResize : function(w, h)
22021     {
22022          Roo.log('resize: ' +w + ',' + h );
22023         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22024         if(!this.iframe){
22025             return;
22026         }
22027         if(typeof w == 'number'){
22028             
22029             this.iframe.style.width = w + 'px';
22030         }
22031         if(typeof h == 'number'){
22032             
22033             this.iframe.style.height = h + 'px';
22034             if(this.doc){
22035                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22036             }
22037         }
22038         
22039     },
22040
22041     /**
22042      * Toggles the editor between standard and source edit mode.
22043      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22044      */
22045     toggleSourceEdit : function(sourceEditMode){
22046         
22047         this.sourceEditMode = sourceEditMode === true;
22048         
22049         if(this.sourceEditMode){
22050  
22051             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22052             
22053         }else{
22054             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22055             //this.iframe.className = '';
22056             this.deferFocus();
22057         }
22058         //this.setSize(this.owner.wrap.getSize());
22059         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22060     },
22061
22062     
22063   
22064
22065     /**
22066      * Protected method that will not generally be called directly. If you need/want
22067      * custom HTML cleanup, this is the method you should override.
22068      * @param {String} html The HTML to be cleaned
22069      * return {String} The cleaned HTML
22070      */
22071     cleanHtml : function(html){
22072         html = String(html);
22073         if(html.length > 5){
22074             if(Roo.isSafari){ // strip safari nonsense
22075                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22076             }
22077         }
22078         if(html == '&nbsp;'){
22079             html = '';
22080         }
22081         return html;
22082     },
22083
22084     /**
22085      * HTML Editor -> Textarea
22086      * Protected method that will not generally be called directly. Syncs the contents
22087      * of the editor iframe with the textarea.
22088      */
22089     syncValue : function(){
22090         if(this.initialized){
22091             var bd = (this.doc.body || this.doc.documentElement);
22092             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22093             var html = bd.innerHTML;
22094             if(Roo.isSafari){
22095                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22096                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22097                 if(m && m[1]){
22098                     html = '<div style="'+m[0]+'">' + html + '</div>';
22099                 }
22100             }
22101             html = this.cleanHtml(html);
22102             // fix up the special chars.. normaly like back quotes in word...
22103             // however we do not want to do this with chinese..
22104             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22105                 var cc = b.charCodeAt();
22106                 if (
22107                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22108                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22109                     (cc >= 0xf900 && cc < 0xfb00 )
22110                 ) {
22111                         return b;
22112                 }
22113                 return "&#"+cc+";" 
22114             });
22115             if(this.owner.fireEvent('beforesync', this, html) !== false){
22116                 this.el.dom.value = html;
22117                 this.owner.fireEvent('sync', this, html);
22118             }
22119         }
22120     },
22121
22122     /**
22123      * Protected method that will not generally be called directly. Pushes the value of the textarea
22124      * into the iframe editor.
22125      */
22126     pushValue : function(){
22127         if(this.initialized){
22128             var v = this.el.dom.value.trim();
22129             
22130 //            if(v.length < 1){
22131 //                v = '&#160;';
22132 //            }
22133             
22134             if(this.owner.fireEvent('beforepush', this, v) !== false){
22135                 var d = (this.doc.body || this.doc.documentElement);
22136                 d.innerHTML = v;
22137                 this.cleanUpPaste();
22138                 this.el.dom.value = d.innerHTML;
22139                 this.owner.fireEvent('push', this, v);
22140             }
22141         }
22142     },
22143
22144     // private
22145     deferFocus : function(){
22146         this.focus.defer(10, this);
22147     },
22148
22149     // doc'ed in Field
22150     focus : function(){
22151         if(this.win && !this.sourceEditMode){
22152             this.win.focus();
22153         }else{
22154             this.el.focus();
22155         }
22156     },
22157     
22158     assignDocWin: function()
22159     {
22160         var iframe = this.iframe;
22161         
22162          if(Roo.isIE){
22163             this.doc = iframe.contentWindow.document;
22164             this.win = iframe.contentWindow;
22165         } else {
22166 //            if (!Roo.get(this.frameId)) {
22167 //                return;
22168 //            }
22169 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22170 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22171             
22172             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22173                 return;
22174             }
22175             
22176             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22177             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22178         }
22179     },
22180     
22181     // private
22182     initEditor : function(){
22183         //console.log("INIT EDITOR");
22184         this.assignDocWin();
22185         
22186         
22187         
22188         this.doc.designMode="on";
22189         this.doc.open();
22190         this.doc.write(this.getDocMarkup());
22191         this.doc.close();
22192         
22193         var dbody = (this.doc.body || this.doc.documentElement);
22194         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22195         // this copies styles from the containing element into thsi one..
22196         // not sure why we need all of this..
22197         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22198         
22199         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22200         //ss['background-attachment'] = 'fixed'; // w3c
22201         dbody.bgProperties = 'fixed'; // ie
22202         //Roo.DomHelper.applyStyles(dbody, ss);
22203         Roo.EventManager.on(this.doc, {
22204             //'mousedown': this.onEditorEvent,
22205             'mouseup': this.onEditorEvent,
22206             'dblclick': this.onEditorEvent,
22207             'click': this.onEditorEvent,
22208             'keyup': this.onEditorEvent,
22209             buffer:100,
22210             scope: this
22211         });
22212         if(Roo.isGecko){
22213             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22214         }
22215         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22216             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22217         }
22218         this.initialized = true;
22219
22220         this.owner.fireEvent('initialize', this);
22221         this.pushValue();
22222     },
22223
22224     // private
22225     onDestroy : function(){
22226         
22227         
22228         
22229         if(this.rendered){
22230             
22231             //for (var i =0; i < this.toolbars.length;i++) {
22232             //    // fixme - ask toolbars for heights?
22233             //    this.toolbars[i].onDestroy();
22234            // }
22235             
22236             //this.wrap.dom.innerHTML = '';
22237             //this.wrap.remove();
22238         }
22239     },
22240
22241     // private
22242     onFirstFocus : function(){
22243         
22244         this.assignDocWin();
22245         
22246         
22247         this.activated = true;
22248          
22249     
22250         if(Roo.isGecko){ // prevent silly gecko errors
22251             this.win.focus();
22252             var s = this.win.getSelection();
22253             if(!s.focusNode || s.focusNode.nodeType != 3){
22254                 var r = s.getRangeAt(0);
22255                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22256                 r.collapse(true);
22257                 this.deferFocus();
22258             }
22259             try{
22260                 this.execCmd('useCSS', true);
22261                 this.execCmd('styleWithCSS', false);
22262             }catch(e){}
22263         }
22264         this.owner.fireEvent('activate', this);
22265     },
22266
22267     // private
22268     adjustFont: function(btn){
22269         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22270         //if(Roo.isSafari){ // safari
22271         //    adjust *= 2;
22272        // }
22273         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22274         if(Roo.isSafari){ // safari
22275             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22276             v =  (v < 10) ? 10 : v;
22277             v =  (v > 48) ? 48 : v;
22278             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22279             
22280         }
22281         
22282         
22283         v = Math.max(1, v+adjust);
22284         
22285         this.execCmd('FontSize', v  );
22286     },
22287
22288     onEditorEvent : function(e)
22289     {
22290         this.owner.fireEvent('editorevent', this, e);
22291       //  this.updateToolbar();
22292         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22293     },
22294
22295     insertTag : function(tg)
22296     {
22297         // could be a bit smarter... -> wrap the current selected tRoo..
22298         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22299             
22300             range = this.createRange(this.getSelection());
22301             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22302             wrappingNode.appendChild(range.extractContents());
22303             range.insertNode(wrappingNode);
22304
22305             return;
22306             
22307             
22308             
22309         }
22310         this.execCmd("formatblock",   tg);
22311         
22312     },
22313     
22314     insertText : function(txt)
22315     {
22316         
22317         
22318         var range = this.createRange();
22319         range.deleteContents();
22320                //alert(Sender.getAttribute('label'));
22321                
22322         range.insertNode(this.doc.createTextNode(txt));
22323     } ,
22324     
22325      
22326
22327     /**
22328      * Executes a Midas editor command on the editor document and performs necessary focus and
22329      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22330      * @param {String} cmd The Midas command
22331      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22332      */
22333     relayCmd : function(cmd, value){
22334         this.win.focus();
22335         this.execCmd(cmd, value);
22336         this.owner.fireEvent('editorevent', this);
22337         //this.updateToolbar();
22338         this.owner.deferFocus();
22339     },
22340
22341     /**
22342      * Executes a Midas editor command directly on the editor document.
22343      * For visual commands, you should use {@link #relayCmd} instead.
22344      * <b>This should only be called after the editor is initialized.</b>
22345      * @param {String} cmd The Midas command
22346      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22347      */
22348     execCmd : function(cmd, value){
22349         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22350         this.syncValue();
22351     },
22352  
22353  
22354    
22355     /**
22356      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22357      * to insert tRoo.
22358      * @param {String} text | dom node.. 
22359      */
22360     insertAtCursor : function(text)
22361     {
22362         
22363         if(!this.activated){
22364             return;
22365         }
22366         /*
22367         if(Roo.isIE){
22368             this.win.focus();
22369             var r = this.doc.selection.createRange();
22370             if(r){
22371                 r.collapse(true);
22372                 r.pasteHTML(text);
22373                 this.syncValue();
22374                 this.deferFocus();
22375             
22376             }
22377             return;
22378         }
22379         */
22380         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22381             this.win.focus();
22382             
22383             
22384             // from jquery ui (MIT licenced)
22385             var range, node;
22386             var win = this.win;
22387             
22388             if (win.getSelection && win.getSelection().getRangeAt) {
22389                 range = win.getSelection().getRangeAt(0);
22390                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22391                 range.insertNode(node);
22392             } else if (win.document.selection && win.document.selection.createRange) {
22393                 // no firefox support
22394                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22395                 win.document.selection.createRange().pasteHTML(txt);
22396             } else {
22397                 // no firefox support
22398                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22399                 this.execCmd('InsertHTML', txt);
22400             } 
22401             
22402             this.syncValue();
22403             
22404             this.deferFocus();
22405         }
22406     },
22407  // private
22408     mozKeyPress : function(e){
22409         if(e.ctrlKey){
22410             var c = e.getCharCode(), cmd;
22411           
22412             if(c > 0){
22413                 c = String.fromCharCode(c).toLowerCase();
22414                 switch(c){
22415                     case 'b':
22416                         cmd = 'bold';
22417                         break;
22418                     case 'i':
22419                         cmd = 'italic';
22420                         break;
22421                     
22422                     case 'u':
22423                         cmd = 'underline';
22424                         break;
22425                     
22426                     case 'v':
22427                         this.cleanUpPaste.defer(100, this);
22428                         return;
22429                         
22430                 }
22431                 if(cmd){
22432                     this.win.focus();
22433                     this.execCmd(cmd);
22434                     this.deferFocus();
22435                     e.preventDefault();
22436                 }
22437                 
22438             }
22439         }
22440     },
22441
22442     // private
22443     fixKeys : function(){ // load time branching for fastest keydown performance
22444         if(Roo.isIE){
22445             return function(e){
22446                 var k = e.getKey(), r;
22447                 if(k == e.TAB){
22448                     e.stopEvent();
22449                     r = this.doc.selection.createRange();
22450                     if(r){
22451                         r.collapse(true);
22452                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22453                         this.deferFocus();
22454                     }
22455                     return;
22456                 }
22457                 
22458                 if(k == e.ENTER){
22459                     r = this.doc.selection.createRange();
22460                     if(r){
22461                         var target = r.parentElement();
22462                         if(!target || target.tagName.toLowerCase() != 'li'){
22463                             e.stopEvent();
22464                             r.pasteHTML('<br />');
22465                             r.collapse(false);
22466                             r.select();
22467                         }
22468                     }
22469                 }
22470                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22471                     this.cleanUpPaste.defer(100, this);
22472                     return;
22473                 }
22474                 
22475                 
22476             };
22477         }else if(Roo.isOpera){
22478             return function(e){
22479                 var k = e.getKey();
22480                 if(k == e.TAB){
22481                     e.stopEvent();
22482                     this.win.focus();
22483                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22484                     this.deferFocus();
22485                 }
22486                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22487                     this.cleanUpPaste.defer(100, this);
22488                     return;
22489                 }
22490                 
22491             };
22492         }else if(Roo.isSafari){
22493             return function(e){
22494                 var k = e.getKey();
22495                 
22496                 if(k == e.TAB){
22497                     e.stopEvent();
22498                     this.execCmd('InsertText','\t');
22499                     this.deferFocus();
22500                     return;
22501                 }
22502                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22503                     this.cleanUpPaste.defer(100, this);
22504                     return;
22505                 }
22506                 
22507              };
22508         }
22509     }(),
22510     
22511     getAllAncestors: function()
22512     {
22513         var p = this.getSelectedNode();
22514         var a = [];
22515         if (!p) {
22516             a.push(p); // push blank onto stack..
22517             p = this.getParentElement();
22518         }
22519         
22520         
22521         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22522             a.push(p);
22523             p = p.parentNode;
22524         }
22525         a.push(this.doc.body);
22526         return a;
22527     },
22528     lastSel : false,
22529     lastSelNode : false,
22530     
22531     
22532     getSelection : function() 
22533     {
22534         this.assignDocWin();
22535         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22536     },
22537     
22538     getSelectedNode: function() 
22539     {
22540         // this may only work on Gecko!!!
22541         
22542         // should we cache this!!!!
22543         
22544         
22545         
22546          
22547         var range = this.createRange(this.getSelection()).cloneRange();
22548         
22549         if (Roo.isIE) {
22550             var parent = range.parentElement();
22551             while (true) {
22552                 var testRange = range.duplicate();
22553                 testRange.moveToElementText(parent);
22554                 if (testRange.inRange(range)) {
22555                     break;
22556                 }
22557                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22558                     break;
22559                 }
22560                 parent = parent.parentElement;
22561             }
22562             return parent;
22563         }
22564         
22565         // is ancestor a text element.
22566         var ac =  range.commonAncestorContainer;
22567         if (ac.nodeType == 3) {
22568             ac = ac.parentNode;
22569         }
22570         
22571         var ar = ac.childNodes;
22572          
22573         var nodes = [];
22574         var other_nodes = [];
22575         var has_other_nodes = false;
22576         for (var i=0;i<ar.length;i++) {
22577             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22578                 continue;
22579             }
22580             // fullly contained node.
22581             
22582             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22583                 nodes.push(ar[i]);
22584                 continue;
22585             }
22586             
22587             // probably selected..
22588             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22589                 other_nodes.push(ar[i]);
22590                 continue;
22591             }
22592             // outer..
22593             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22594                 continue;
22595             }
22596             
22597             
22598             has_other_nodes = true;
22599         }
22600         if (!nodes.length && other_nodes.length) {
22601             nodes= other_nodes;
22602         }
22603         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22604             return false;
22605         }
22606         
22607         return nodes[0];
22608     },
22609     createRange: function(sel)
22610     {
22611         // this has strange effects when using with 
22612         // top toolbar - not sure if it's a great idea.
22613         //this.editor.contentWindow.focus();
22614         if (typeof sel != "undefined") {
22615             try {
22616                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22617             } catch(e) {
22618                 return this.doc.createRange();
22619             }
22620         } else {
22621             return this.doc.createRange();
22622         }
22623     },
22624     getParentElement: function()
22625     {
22626         
22627         this.assignDocWin();
22628         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22629         
22630         var range = this.createRange(sel);
22631          
22632         try {
22633             var p = range.commonAncestorContainer;
22634             while (p.nodeType == 3) { // text node
22635                 p = p.parentNode;
22636             }
22637             return p;
22638         } catch (e) {
22639             return null;
22640         }
22641     
22642     },
22643     /***
22644      *
22645      * Range intersection.. the hard stuff...
22646      *  '-1' = before
22647      *  '0' = hits..
22648      *  '1' = after.
22649      *         [ -- selected range --- ]
22650      *   [fail]                        [fail]
22651      *
22652      *    basically..
22653      *      if end is before start or  hits it. fail.
22654      *      if start is after end or hits it fail.
22655      *
22656      *   if either hits (but other is outside. - then it's not 
22657      *   
22658      *    
22659      **/
22660     
22661     
22662     // @see http://www.thismuchiknow.co.uk/?p=64.
22663     rangeIntersectsNode : function(range, node)
22664     {
22665         var nodeRange = node.ownerDocument.createRange();
22666         try {
22667             nodeRange.selectNode(node);
22668         } catch (e) {
22669             nodeRange.selectNodeContents(node);
22670         }
22671     
22672         var rangeStartRange = range.cloneRange();
22673         rangeStartRange.collapse(true);
22674     
22675         var rangeEndRange = range.cloneRange();
22676         rangeEndRange.collapse(false);
22677     
22678         var nodeStartRange = nodeRange.cloneRange();
22679         nodeStartRange.collapse(true);
22680     
22681         var nodeEndRange = nodeRange.cloneRange();
22682         nodeEndRange.collapse(false);
22683     
22684         return rangeStartRange.compareBoundaryPoints(
22685                  Range.START_TO_START, nodeEndRange) == -1 &&
22686                rangeEndRange.compareBoundaryPoints(
22687                  Range.START_TO_START, nodeStartRange) == 1;
22688         
22689          
22690     },
22691     rangeCompareNode : function(range, node)
22692     {
22693         var nodeRange = node.ownerDocument.createRange();
22694         try {
22695             nodeRange.selectNode(node);
22696         } catch (e) {
22697             nodeRange.selectNodeContents(node);
22698         }
22699         
22700         
22701         range.collapse(true);
22702     
22703         nodeRange.collapse(true);
22704      
22705         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22706         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22707          
22708         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22709         
22710         var nodeIsBefore   =  ss == 1;
22711         var nodeIsAfter    = ee == -1;
22712         
22713         if (nodeIsBefore && nodeIsAfter) {
22714             return 0; // outer
22715         }
22716         if (!nodeIsBefore && nodeIsAfter) {
22717             return 1; //right trailed.
22718         }
22719         
22720         if (nodeIsBefore && !nodeIsAfter) {
22721             return 2;  // left trailed.
22722         }
22723         // fully contined.
22724         return 3;
22725     },
22726
22727     // private? - in a new class?
22728     cleanUpPaste :  function()
22729     {
22730         // cleans up the whole document..
22731         Roo.log('cleanuppaste');
22732         
22733         this.cleanUpChildren(this.doc.body);
22734         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22735         if (clean != this.doc.body.innerHTML) {
22736             this.doc.body.innerHTML = clean;
22737         }
22738         
22739     },
22740     
22741     cleanWordChars : function(input) {// change the chars to hex code
22742         var he = Roo.HtmlEditorCore;
22743         
22744         var output = input;
22745         Roo.each(he.swapCodes, function(sw) { 
22746             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22747             
22748             output = output.replace(swapper, sw[1]);
22749         });
22750         
22751         return output;
22752     },
22753     
22754     
22755     cleanUpChildren : function (n)
22756     {
22757         if (!n.childNodes.length) {
22758             return;
22759         }
22760         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22761            this.cleanUpChild(n.childNodes[i]);
22762         }
22763     },
22764     
22765     
22766         
22767     
22768     cleanUpChild : function (node)
22769     {
22770         var ed = this;
22771         //console.log(node);
22772         if (node.nodeName == "#text") {
22773             // clean up silly Windows -- stuff?
22774             return; 
22775         }
22776         if (node.nodeName == "#comment") {
22777             node.parentNode.removeChild(node);
22778             // clean up silly Windows -- stuff?
22779             return; 
22780         }
22781         var lcname = node.tagName.toLowerCase();
22782         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22783         // whitelist of tags..
22784         
22785         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22786             // remove node.
22787             node.parentNode.removeChild(node);
22788             return;
22789             
22790         }
22791         
22792         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22793         
22794         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22795         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22796         
22797         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22798         //    remove_keep_children = true;
22799         //}
22800         
22801         if (remove_keep_children) {
22802             this.cleanUpChildren(node);
22803             // inserts everything just before this node...
22804             while (node.childNodes.length) {
22805                 var cn = node.childNodes[0];
22806                 node.removeChild(cn);
22807                 node.parentNode.insertBefore(cn, node);
22808             }
22809             node.parentNode.removeChild(node);
22810             return;
22811         }
22812         
22813         if (!node.attributes || !node.attributes.length) {
22814             this.cleanUpChildren(node);
22815             return;
22816         }
22817         
22818         function cleanAttr(n,v)
22819         {
22820             
22821             if (v.match(/^\./) || v.match(/^\//)) {
22822                 return;
22823             }
22824             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22825                 return;
22826             }
22827             if (v.match(/^#/)) {
22828                 return;
22829             }
22830 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22831             node.removeAttribute(n);
22832             
22833         }
22834         
22835         var cwhite = this.cwhite;
22836         var cblack = this.cblack;
22837             
22838         function cleanStyle(n,v)
22839         {
22840             if (v.match(/expression/)) { //XSS?? should we even bother..
22841                 node.removeAttribute(n);
22842                 return;
22843             }
22844             
22845             var parts = v.split(/;/);
22846             var clean = [];
22847             
22848             Roo.each(parts, function(p) {
22849                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22850                 if (!p.length) {
22851                     return true;
22852                 }
22853                 var l = p.split(':').shift().replace(/\s+/g,'');
22854                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22855                 
22856                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22857 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22858                     //node.removeAttribute(n);
22859                     return true;
22860                 }
22861                 //Roo.log()
22862                 // only allow 'c whitelisted system attributes'
22863                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22864 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22865                     //node.removeAttribute(n);
22866                     return true;
22867                 }
22868                 
22869                 
22870                  
22871                 
22872                 clean.push(p);
22873                 return true;
22874             });
22875             if (clean.length) { 
22876                 node.setAttribute(n, clean.join(';'));
22877             } else {
22878                 node.removeAttribute(n);
22879             }
22880             
22881         }
22882         
22883         
22884         for (var i = node.attributes.length-1; i > -1 ; i--) {
22885             var a = node.attributes[i];
22886             //console.log(a);
22887             
22888             if (a.name.toLowerCase().substr(0,2)=='on')  {
22889                 node.removeAttribute(a.name);
22890                 continue;
22891             }
22892             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22893                 node.removeAttribute(a.name);
22894                 continue;
22895             }
22896             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22897                 cleanAttr(a.name,a.value); // fixme..
22898                 continue;
22899             }
22900             if (a.name == 'style') {
22901                 cleanStyle(a.name,a.value);
22902                 continue;
22903             }
22904             /// clean up MS crap..
22905             // tecnically this should be a list of valid class'es..
22906             
22907             
22908             if (a.name == 'class') {
22909                 if (a.value.match(/^Mso/)) {
22910                     node.className = '';
22911                 }
22912                 
22913                 if (a.value.match(/^body$/)) {
22914                     node.className = '';
22915                 }
22916                 continue;
22917             }
22918             
22919             // style cleanup!?
22920             // class cleanup?
22921             
22922         }
22923         
22924         
22925         this.cleanUpChildren(node);
22926         
22927         
22928     },
22929     
22930     /**
22931      * Clean up MS wordisms...
22932      */
22933     cleanWord : function(node)
22934     {
22935         
22936         
22937         if (!node) {
22938             this.cleanWord(this.doc.body);
22939             return;
22940         }
22941         if (node.nodeName == "#text") {
22942             // clean up silly Windows -- stuff?
22943             return; 
22944         }
22945         if (node.nodeName == "#comment") {
22946             node.parentNode.removeChild(node);
22947             // clean up silly Windows -- stuff?
22948             return; 
22949         }
22950         
22951         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22952             node.parentNode.removeChild(node);
22953             return;
22954         }
22955         
22956         // remove - but keep children..
22957         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22958             while (node.childNodes.length) {
22959                 var cn = node.childNodes[0];
22960                 node.removeChild(cn);
22961                 node.parentNode.insertBefore(cn, node);
22962             }
22963             node.parentNode.removeChild(node);
22964             this.iterateChildren(node, this.cleanWord);
22965             return;
22966         }
22967         // clean styles
22968         if (node.className.length) {
22969             
22970             var cn = node.className.split(/\W+/);
22971             var cna = [];
22972             Roo.each(cn, function(cls) {
22973                 if (cls.match(/Mso[a-zA-Z]+/)) {
22974                     return;
22975                 }
22976                 cna.push(cls);
22977             });
22978             node.className = cna.length ? cna.join(' ') : '';
22979             if (!cna.length) {
22980                 node.removeAttribute("class");
22981             }
22982         }
22983         
22984         if (node.hasAttribute("lang")) {
22985             node.removeAttribute("lang");
22986         }
22987         
22988         if (node.hasAttribute("style")) {
22989             
22990             var styles = node.getAttribute("style").split(";");
22991             var nstyle = [];
22992             Roo.each(styles, function(s) {
22993                 if (!s.match(/:/)) {
22994                     return;
22995                 }
22996                 var kv = s.split(":");
22997                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22998                     return;
22999                 }
23000                 // what ever is left... we allow.
23001                 nstyle.push(s);
23002             });
23003             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23004             if (!nstyle.length) {
23005                 node.removeAttribute('style');
23006             }
23007         }
23008         this.iterateChildren(node, this.cleanWord);
23009         
23010         
23011         
23012     },
23013     /**
23014      * iterateChildren of a Node, calling fn each time, using this as the scole..
23015      * @param {DomNode} node node to iterate children of.
23016      * @param {Function} fn method of this class to call on each item.
23017      */
23018     iterateChildren : function(node, fn)
23019     {
23020         if (!node.childNodes.length) {
23021                 return;
23022         }
23023         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23024            fn.call(this, node.childNodes[i])
23025         }
23026     },
23027     
23028     
23029     /**
23030      * cleanTableWidths.
23031      *
23032      * Quite often pasting from word etc.. results in tables with column and widths.
23033      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23034      *
23035      */
23036     cleanTableWidths : function(node)
23037     {
23038          
23039          
23040         if (!node) {
23041             this.cleanTableWidths(this.doc.body);
23042             return;
23043         }
23044         
23045         // ignore list...
23046         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23047             return; 
23048         }
23049         Roo.log(node.tagName);
23050         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23051             this.iterateChildren(node, this.cleanTableWidths);
23052             return;
23053         }
23054         if (node.hasAttribute('width')) {
23055             node.removeAttribute('width');
23056         }
23057         
23058          
23059         if (node.hasAttribute("style")) {
23060             // pretty basic...
23061             
23062             var styles = node.getAttribute("style").split(";");
23063             var nstyle = [];
23064             Roo.each(styles, function(s) {
23065                 if (!s.match(/:/)) {
23066                     return;
23067                 }
23068                 var kv = s.split(":");
23069                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23070                     return;
23071                 }
23072                 // what ever is left... we allow.
23073                 nstyle.push(s);
23074             });
23075             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23076             if (!nstyle.length) {
23077                 node.removeAttribute('style');
23078             }
23079         }
23080         
23081         this.iterateChildren(node, this.cleanTableWidths);
23082         
23083         
23084     },
23085     
23086     
23087     
23088     
23089     domToHTML : function(currentElement, depth, nopadtext) {
23090         
23091         depth = depth || 0;
23092         nopadtext = nopadtext || false;
23093     
23094         if (!currentElement) {
23095             return this.domToHTML(this.doc.body);
23096         }
23097         
23098         //Roo.log(currentElement);
23099         var j;
23100         var allText = false;
23101         var nodeName = currentElement.nodeName;
23102         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23103         
23104         if  (nodeName == '#text') {
23105             
23106             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23107         }
23108         
23109         
23110         var ret = '';
23111         if (nodeName != 'BODY') {
23112              
23113             var i = 0;
23114             // Prints the node tagName, such as <A>, <IMG>, etc
23115             if (tagName) {
23116                 var attr = [];
23117                 for(i = 0; i < currentElement.attributes.length;i++) {
23118                     // quoting?
23119                     var aname = currentElement.attributes.item(i).name;
23120                     if (!currentElement.attributes.item(i).value.length) {
23121                         continue;
23122                     }
23123                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23124                 }
23125                 
23126                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23127             } 
23128             else {
23129                 
23130                 // eack
23131             }
23132         } else {
23133             tagName = false;
23134         }
23135         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23136             return ret;
23137         }
23138         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23139             nopadtext = true;
23140         }
23141         
23142         
23143         // Traverse the tree
23144         i = 0;
23145         var currentElementChild = currentElement.childNodes.item(i);
23146         var allText = true;
23147         var innerHTML  = '';
23148         lastnode = '';
23149         while (currentElementChild) {
23150             // Formatting code (indent the tree so it looks nice on the screen)
23151             var nopad = nopadtext;
23152             if (lastnode == 'SPAN') {
23153                 nopad  = true;
23154             }
23155             // text
23156             if  (currentElementChild.nodeName == '#text') {
23157                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23158                 toadd = nopadtext ? toadd : toadd.trim();
23159                 if (!nopad && toadd.length > 80) {
23160                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23161                 }
23162                 innerHTML  += toadd;
23163                 
23164                 i++;
23165                 currentElementChild = currentElement.childNodes.item(i);
23166                 lastNode = '';
23167                 continue;
23168             }
23169             allText = false;
23170             
23171             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23172                 
23173             // Recursively traverse the tree structure of the child node
23174             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23175             lastnode = currentElementChild.nodeName;
23176             i++;
23177             currentElementChild=currentElement.childNodes.item(i);
23178         }
23179         
23180         ret += innerHTML;
23181         
23182         if (!allText) {
23183                 // The remaining code is mostly for formatting the tree
23184             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23185         }
23186         
23187         
23188         if (tagName) {
23189             ret+= "</"+tagName+">";
23190         }
23191         return ret;
23192         
23193     },
23194         
23195     applyBlacklists : function()
23196     {
23197         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23198         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23199         
23200         this.white = [];
23201         this.black = [];
23202         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23203             if (b.indexOf(tag) > -1) {
23204                 return;
23205             }
23206             this.white.push(tag);
23207             
23208         }, this);
23209         
23210         Roo.each(w, function(tag) {
23211             if (b.indexOf(tag) > -1) {
23212                 return;
23213             }
23214             if (this.white.indexOf(tag) > -1) {
23215                 return;
23216             }
23217             this.white.push(tag);
23218             
23219         }, this);
23220         
23221         
23222         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23223             if (w.indexOf(tag) > -1) {
23224                 return;
23225             }
23226             this.black.push(tag);
23227             
23228         }, this);
23229         
23230         Roo.each(b, function(tag) {
23231             if (w.indexOf(tag) > -1) {
23232                 return;
23233             }
23234             if (this.black.indexOf(tag) > -1) {
23235                 return;
23236             }
23237             this.black.push(tag);
23238             
23239         }, this);
23240         
23241         
23242         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23243         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23244         
23245         this.cwhite = [];
23246         this.cblack = [];
23247         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23248             if (b.indexOf(tag) > -1) {
23249                 return;
23250             }
23251             this.cwhite.push(tag);
23252             
23253         }, this);
23254         
23255         Roo.each(w, function(tag) {
23256             if (b.indexOf(tag) > -1) {
23257                 return;
23258             }
23259             if (this.cwhite.indexOf(tag) > -1) {
23260                 return;
23261             }
23262             this.cwhite.push(tag);
23263             
23264         }, this);
23265         
23266         
23267         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23268             if (w.indexOf(tag) > -1) {
23269                 return;
23270             }
23271             this.cblack.push(tag);
23272             
23273         }, this);
23274         
23275         Roo.each(b, function(tag) {
23276             if (w.indexOf(tag) > -1) {
23277                 return;
23278             }
23279             if (this.cblack.indexOf(tag) > -1) {
23280                 return;
23281             }
23282             this.cblack.push(tag);
23283             
23284         }, this);
23285     },
23286     
23287     setStylesheets : function(stylesheets)
23288     {
23289         if(typeof(stylesheets) == 'string'){
23290             Roo.get(this.iframe.contentDocument.head).createChild({
23291                 tag : 'link',
23292                 rel : 'stylesheet',
23293                 type : 'text/css',
23294                 href : stylesheets
23295             });
23296             
23297             return;
23298         }
23299         var _this = this;
23300      
23301         Roo.each(stylesheets, function(s) {
23302             if(!s.length){
23303                 return;
23304             }
23305             
23306             Roo.get(_this.iframe.contentDocument.head).createChild({
23307                 tag : 'link',
23308                 rel : 'stylesheet',
23309                 type : 'text/css',
23310                 href : s
23311             });
23312         });
23313
23314         
23315     },
23316     
23317     removeStylesheets : function()
23318     {
23319         var _this = this;
23320         
23321         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23322             s.remove();
23323         });
23324     },
23325     
23326     setStyle : function(style)
23327     {
23328         Roo.get(this.iframe.contentDocument.head).createChild({
23329             tag : 'style',
23330             type : 'text/css',
23331             html : style
23332         });
23333
23334         return;
23335     }
23336     
23337     // hide stuff that is not compatible
23338     /**
23339      * @event blur
23340      * @hide
23341      */
23342     /**
23343      * @event change
23344      * @hide
23345      */
23346     /**
23347      * @event focus
23348      * @hide
23349      */
23350     /**
23351      * @event specialkey
23352      * @hide
23353      */
23354     /**
23355      * @cfg {String} fieldClass @hide
23356      */
23357     /**
23358      * @cfg {String} focusClass @hide
23359      */
23360     /**
23361      * @cfg {String} autoCreate @hide
23362      */
23363     /**
23364      * @cfg {String} inputType @hide
23365      */
23366     /**
23367      * @cfg {String} invalidClass @hide
23368      */
23369     /**
23370      * @cfg {String} invalidText @hide
23371      */
23372     /**
23373      * @cfg {String} msgFx @hide
23374      */
23375     /**
23376      * @cfg {String} validateOnBlur @hide
23377      */
23378 });
23379
23380 Roo.HtmlEditorCore.white = [
23381         'area', 'br', 'img', 'input', 'hr', 'wbr',
23382         
23383        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23384        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23385        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23386        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23387        'table',   'ul',         'xmp', 
23388        
23389        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23390       'thead',   'tr', 
23391      
23392       'dir', 'menu', 'ol', 'ul', 'dl',
23393        
23394       'embed',  'object'
23395 ];
23396
23397
23398 Roo.HtmlEditorCore.black = [
23399     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23400         'applet', // 
23401         'base',   'basefont', 'bgsound', 'blink',  'body', 
23402         'frame',  'frameset', 'head',    'html',   'ilayer', 
23403         'iframe', 'layer',  'link',     'meta',    'object',   
23404         'script', 'style' ,'title',  'xml' // clean later..
23405 ];
23406 Roo.HtmlEditorCore.clean = [
23407     'script', 'style', 'title', 'xml'
23408 ];
23409 Roo.HtmlEditorCore.remove = [
23410     'font'
23411 ];
23412 // attributes..
23413
23414 Roo.HtmlEditorCore.ablack = [
23415     'on'
23416 ];
23417     
23418 Roo.HtmlEditorCore.aclean = [ 
23419     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23420 ];
23421
23422 // protocols..
23423 Roo.HtmlEditorCore.pwhite= [
23424         'http',  'https',  'mailto'
23425 ];
23426
23427 // white listed style attributes.
23428 Roo.HtmlEditorCore.cwhite= [
23429       //  'text-align', /// default is to allow most things..
23430       
23431          
23432 //        'font-size'//??
23433 ];
23434
23435 // black listed style attributes.
23436 Roo.HtmlEditorCore.cblack= [
23437       //  'font-size' -- this can be set by the project 
23438 ];
23439
23440
23441 Roo.HtmlEditorCore.swapCodes   =[ 
23442     [    8211, "--" ], 
23443     [    8212, "--" ], 
23444     [    8216,  "'" ],  
23445     [    8217, "'" ],  
23446     [    8220, '"' ],  
23447     [    8221, '"' ],  
23448     [    8226, "*" ],  
23449     [    8230, "..." ]
23450 ]; 
23451
23452     /*
23453  * - LGPL
23454  *
23455  * HtmlEditor
23456  * 
23457  */
23458
23459 /**
23460  * @class Roo.bootstrap.HtmlEditor
23461  * @extends Roo.bootstrap.TextArea
23462  * Bootstrap HtmlEditor class
23463
23464  * @constructor
23465  * Create a new HtmlEditor
23466  * @param {Object} config The config object
23467  */
23468
23469 Roo.bootstrap.HtmlEditor = function(config){
23470     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23471     if (!this.toolbars) {
23472         this.toolbars = [];
23473     }
23474     
23475     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23476     this.addEvents({
23477             /**
23478              * @event initialize
23479              * Fires when the editor is fully initialized (including the iframe)
23480              * @param {HtmlEditor} this
23481              */
23482             initialize: true,
23483             /**
23484              * @event activate
23485              * Fires when the editor is first receives the focus. Any insertion must wait
23486              * until after this event.
23487              * @param {HtmlEditor} this
23488              */
23489             activate: true,
23490              /**
23491              * @event beforesync
23492              * Fires before the textarea is updated with content from the editor iframe. Return false
23493              * to cancel the sync.
23494              * @param {HtmlEditor} this
23495              * @param {String} html
23496              */
23497             beforesync: true,
23498              /**
23499              * @event beforepush
23500              * Fires before the iframe editor is updated with content from the textarea. Return false
23501              * to cancel the push.
23502              * @param {HtmlEditor} this
23503              * @param {String} html
23504              */
23505             beforepush: true,
23506              /**
23507              * @event sync
23508              * Fires when the textarea is updated with content from the editor iframe.
23509              * @param {HtmlEditor} this
23510              * @param {String} html
23511              */
23512             sync: true,
23513              /**
23514              * @event push
23515              * Fires when the iframe editor is updated with content from the textarea.
23516              * @param {HtmlEditor} this
23517              * @param {String} html
23518              */
23519             push: true,
23520              /**
23521              * @event editmodechange
23522              * Fires when the editor switches edit modes
23523              * @param {HtmlEditor} this
23524              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23525              */
23526             editmodechange: true,
23527             /**
23528              * @event editorevent
23529              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23530              * @param {HtmlEditor} this
23531              */
23532             editorevent: true,
23533             /**
23534              * @event firstfocus
23535              * Fires when on first focus - needed by toolbars..
23536              * @param {HtmlEditor} this
23537              */
23538             firstfocus: true,
23539             /**
23540              * @event autosave
23541              * Auto save the htmlEditor value as a file into Events
23542              * @param {HtmlEditor} this
23543              */
23544             autosave: true,
23545             /**
23546              * @event savedpreview
23547              * preview the saved version of htmlEditor
23548              * @param {HtmlEditor} this
23549              */
23550             savedpreview: true
23551         });
23552 };
23553
23554
23555 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23556     
23557     
23558       /**
23559      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23560      */
23561     toolbars : false,
23562     
23563      /**
23564     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23565     */
23566     btns : [],
23567    
23568      /**
23569      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23570      *                        Roo.resizable.
23571      */
23572     resizable : false,
23573      /**
23574      * @cfg {Number} height (in pixels)
23575      */   
23576     height: 300,
23577    /**
23578      * @cfg {Number} width (in pixels)
23579      */   
23580     width: false,
23581     
23582     /**
23583      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23584      * 
23585      */
23586     stylesheets: false,
23587     
23588     // id of frame..
23589     frameId: false,
23590     
23591     // private properties
23592     validationEvent : false,
23593     deferHeight: true,
23594     initialized : false,
23595     activated : false,
23596     
23597     onFocus : Roo.emptyFn,
23598     iframePad:3,
23599     hideMode:'offsets',
23600     
23601     tbContainer : false,
23602     
23603     bodyCls : '',
23604     
23605     toolbarContainer :function() {
23606         return this.wrap.select('.x-html-editor-tb',true).first();
23607     },
23608
23609     /**
23610      * Protected method that will not generally be called directly. It
23611      * is called when the editor creates its toolbar. Override this method if you need to
23612      * add custom toolbar buttons.
23613      * @param {HtmlEditor} editor
23614      */
23615     createToolbar : function(){
23616         Roo.log('renewing');
23617         Roo.log("create toolbars");
23618         
23619         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23620         this.toolbars[0].render(this.toolbarContainer());
23621         
23622         return;
23623         
23624 //        if (!editor.toolbars || !editor.toolbars.length) {
23625 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23626 //        }
23627 //        
23628 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23629 //            editor.toolbars[i] = Roo.factory(
23630 //                    typeof(editor.toolbars[i]) == 'string' ?
23631 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23632 //                Roo.bootstrap.HtmlEditor);
23633 //            editor.toolbars[i].init(editor);
23634 //        }
23635     },
23636
23637      
23638     // private
23639     onRender : function(ct, position)
23640     {
23641        // Roo.log("Call onRender: " + this.xtype);
23642         var _t = this;
23643         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23644       
23645         this.wrap = this.inputEl().wrap({
23646             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23647         });
23648         
23649         this.editorcore.onRender(ct, position);
23650          
23651         if (this.resizable) {
23652             this.resizeEl = new Roo.Resizable(this.wrap, {
23653                 pinned : true,
23654                 wrap: true,
23655                 dynamic : true,
23656                 minHeight : this.height,
23657                 height: this.height,
23658                 handles : this.resizable,
23659                 width: this.width,
23660                 listeners : {
23661                     resize : function(r, w, h) {
23662                         _t.onResize(w,h); // -something
23663                     }
23664                 }
23665             });
23666             
23667         }
23668         this.createToolbar(this);
23669        
23670         
23671         if(!this.width && this.resizable){
23672             this.setSize(this.wrap.getSize());
23673         }
23674         if (this.resizeEl) {
23675             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23676             // should trigger onReize..
23677         }
23678         
23679     },
23680
23681     // private
23682     onResize : function(w, h)
23683     {
23684         Roo.log('resize: ' +w + ',' + h );
23685         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23686         var ew = false;
23687         var eh = false;
23688         
23689         if(this.inputEl() ){
23690             if(typeof w == 'number'){
23691                 var aw = w - this.wrap.getFrameWidth('lr');
23692                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23693                 ew = aw;
23694             }
23695             if(typeof h == 'number'){
23696                  var tbh = -11;  // fixme it needs to tool bar size!
23697                 for (var i =0; i < this.toolbars.length;i++) {
23698                     // fixme - ask toolbars for heights?
23699                     tbh += this.toolbars[i].el.getHeight();
23700                     //if (this.toolbars[i].footer) {
23701                     //    tbh += this.toolbars[i].footer.el.getHeight();
23702                     //}
23703                 }
23704               
23705                 
23706                 
23707                 
23708                 
23709                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23710                 ah -= 5; // knock a few pixes off for look..
23711                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23712                 var eh = ah;
23713             }
23714         }
23715         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23716         this.editorcore.onResize(ew,eh);
23717         
23718     },
23719
23720     /**
23721      * Toggles the editor between standard and source edit mode.
23722      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23723      */
23724     toggleSourceEdit : function(sourceEditMode)
23725     {
23726         this.editorcore.toggleSourceEdit(sourceEditMode);
23727         
23728         if(this.editorcore.sourceEditMode){
23729             Roo.log('editor - showing textarea');
23730             
23731 //            Roo.log('in');
23732 //            Roo.log(this.syncValue());
23733             this.syncValue();
23734             this.inputEl().removeClass(['hide', 'x-hidden']);
23735             this.inputEl().dom.removeAttribute('tabIndex');
23736             this.inputEl().focus();
23737         }else{
23738             Roo.log('editor - hiding textarea');
23739 //            Roo.log('out')
23740 //            Roo.log(this.pushValue()); 
23741             this.pushValue();
23742             
23743             this.inputEl().addClass(['hide', 'x-hidden']);
23744             this.inputEl().dom.setAttribute('tabIndex', -1);
23745             //this.deferFocus();
23746         }
23747          
23748         if(this.resizable){
23749             this.setSize(this.wrap.getSize());
23750         }
23751         
23752         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23753     },
23754  
23755     // private (for BoxComponent)
23756     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23757
23758     // private (for BoxComponent)
23759     getResizeEl : function(){
23760         return this.wrap;
23761     },
23762
23763     // private (for BoxComponent)
23764     getPositionEl : function(){
23765         return this.wrap;
23766     },
23767
23768     // private
23769     initEvents : function(){
23770         this.originalValue = this.getValue();
23771     },
23772
23773 //    /**
23774 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23775 //     * @method
23776 //     */
23777 //    markInvalid : Roo.emptyFn,
23778 //    /**
23779 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23780 //     * @method
23781 //     */
23782 //    clearInvalid : Roo.emptyFn,
23783
23784     setValue : function(v){
23785         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23786         this.editorcore.pushValue();
23787     },
23788
23789      
23790     // private
23791     deferFocus : function(){
23792         this.focus.defer(10, this);
23793     },
23794
23795     // doc'ed in Field
23796     focus : function(){
23797         this.editorcore.focus();
23798         
23799     },
23800       
23801
23802     // private
23803     onDestroy : function(){
23804         
23805         
23806         
23807         if(this.rendered){
23808             
23809             for (var i =0; i < this.toolbars.length;i++) {
23810                 // fixme - ask toolbars for heights?
23811                 this.toolbars[i].onDestroy();
23812             }
23813             
23814             this.wrap.dom.innerHTML = '';
23815             this.wrap.remove();
23816         }
23817     },
23818
23819     // private
23820     onFirstFocus : function(){
23821         //Roo.log("onFirstFocus");
23822         this.editorcore.onFirstFocus();
23823          for (var i =0; i < this.toolbars.length;i++) {
23824             this.toolbars[i].onFirstFocus();
23825         }
23826         
23827     },
23828     
23829     // private
23830     syncValue : function()
23831     {   
23832         this.editorcore.syncValue();
23833     },
23834     
23835     pushValue : function()
23836     {   
23837         this.editorcore.pushValue();
23838     }
23839      
23840     
23841     // hide stuff that is not compatible
23842     /**
23843      * @event blur
23844      * @hide
23845      */
23846     /**
23847      * @event change
23848      * @hide
23849      */
23850     /**
23851      * @event focus
23852      * @hide
23853      */
23854     /**
23855      * @event specialkey
23856      * @hide
23857      */
23858     /**
23859      * @cfg {String} fieldClass @hide
23860      */
23861     /**
23862      * @cfg {String} focusClass @hide
23863      */
23864     /**
23865      * @cfg {String} autoCreate @hide
23866      */
23867     /**
23868      * @cfg {String} inputType @hide
23869      */
23870     /**
23871      * @cfg {String} invalidClass @hide
23872      */
23873     /**
23874      * @cfg {String} invalidText @hide
23875      */
23876     /**
23877      * @cfg {String} msgFx @hide
23878      */
23879     /**
23880      * @cfg {String} validateOnBlur @hide
23881      */
23882 });
23883  
23884     
23885    
23886    
23887    
23888       
23889 Roo.namespace('Roo.bootstrap.htmleditor');
23890 /**
23891  * @class Roo.bootstrap.HtmlEditorToolbar1
23892  * Basic Toolbar
23893  * 
23894  * Usage:
23895  *
23896  new Roo.bootstrap.HtmlEditor({
23897     ....
23898     toolbars : [
23899         new Roo.bootstrap.HtmlEditorToolbar1({
23900             disable : { fonts: 1 , format: 1, ..., ... , ...],
23901             btns : [ .... ]
23902         })
23903     }
23904      
23905  * 
23906  * @cfg {Object} disable List of elements to disable..
23907  * @cfg {Array} btns List of additional buttons.
23908  * 
23909  * 
23910  * NEEDS Extra CSS? 
23911  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23912  */
23913  
23914 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23915 {
23916     
23917     Roo.apply(this, config);
23918     
23919     // default disabled, based on 'good practice'..
23920     this.disable = this.disable || {};
23921     Roo.applyIf(this.disable, {
23922         fontSize : true,
23923         colors : true,
23924         specialElements : true
23925     });
23926     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23927     
23928     this.editor = config.editor;
23929     this.editorcore = config.editor.editorcore;
23930     
23931     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23932     
23933     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23934     // dont call parent... till later.
23935 }
23936 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23937      
23938     bar : true,
23939     
23940     editor : false,
23941     editorcore : false,
23942     
23943     
23944     formats : [
23945         "p" ,  
23946         "h1","h2","h3","h4","h5","h6", 
23947         "pre", "code", 
23948         "abbr", "acronym", "address", "cite", "samp", "var",
23949         'div','span'
23950     ],
23951     
23952     onRender : function(ct, position)
23953     {
23954        // Roo.log("Call onRender: " + this.xtype);
23955         
23956        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23957        Roo.log(this.el);
23958        this.el.dom.style.marginBottom = '0';
23959        var _this = this;
23960        var editorcore = this.editorcore;
23961        var editor= this.editor;
23962        
23963        var children = [];
23964        var btn = function(id,cmd , toggle, handler, html){
23965        
23966             var  event = toggle ? 'toggle' : 'click';
23967        
23968             var a = {
23969                 size : 'sm',
23970                 xtype: 'Button',
23971                 xns: Roo.bootstrap,
23972                 //glyphicon : id,
23973                 fa: id,
23974                 cmd : id || cmd,
23975                 enableToggle:toggle !== false,
23976                 html : html || '',
23977                 pressed : toggle ? false : null,
23978                 listeners : {}
23979             };
23980             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23981                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23982             };
23983             children.push(a);
23984             return a;
23985        }
23986        
23987     //    var cb_box = function...
23988         
23989         var style = {
23990                 xtype: 'Button',
23991                 size : 'sm',
23992                 xns: Roo.bootstrap,
23993                 fa : 'font',
23994                 //html : 'submit'
23995                 menu : {
23996                     xtype: 'Menu',
23997                     xns: Roo.bootstrap,
23998                     items:  []
23999                 }
24000         };
24001         Roo.each(this.formats, function(f) {
24002             style.menu.items.push({
24003                 xtype :'MenuItem',
24004                 xns: Roo.bootstrap,
24005                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24006                 tagname : f,
24007                 listeners : {
24008                     click : function()
24009                     {
24010                         editorcore.insertTag(this.tagname);
24011                         editor.focus();
24012                     }
24013                 }
24014                 
24015             });
24016         });
24017         children.push(style);   
24018         
24019         btn('bold',false,true);
24020         btn('italic',false,true);
24021         btn('align-left', 'justifyleft',true);
24022         btn('align-center', 'justifycenter',true);
24023         btn('align-right' , 'justifyright',true);
24024         btn('link', false, false, function(btn) {
24025             //Roo.log("create link?");
24026             var url = prompt(this.createLinkText, this.defaultLinkValue);
24027             if(url && url != 'http:/'+'/'){
24028                 this.editorcore.relayCmd('createlink', url);
24029             }
24030         }),
24031         btn('list','insertunorderedlist',true);
24032         btn('pencil', false,true, function(btn){
24033                 Roo.log(this);
24034                 this.toggleSourceEdit(btn.pressed);
24035         });
24036         
24037         if (this.editor.btns.length > 0) {
24038             for (var i = 0; i<this.editor.btns.length; i++) {
24039                 children.push(this.editor.btns[i]);
24040             }
24041         }
24042         
24043         /*
24044         var cog = {
24045                 xtype: 'Button',
24046                 size : 'sm',
24047                 xns: Roo.bootstrap,
24048                 glyphicon : 'cog',
24049                 //html : 'submit'
24050                 menu : {
24051                     xtype: 'Menu',
24052                     xns: Roo.bootstrap,
24053                     items:  []
24054                 }
24055         };
24056         
24057         cog.menu.items.push({
24058             xtype :'MenuItem',
24059             xns: Roo.bootstrap,
24060             html : Clean styles,
24061             tagname : f,
24062             listeners : {
24063                 click : function()
24064                 {
24065                     editorcore.insertTag(this.tagname);
24066                     editor.focus();
24067                 }
24068             }
24069             
24070         });
24071        */
24072         
24073          
24074        this.xtype = 'NavSimplebar';
24075         
24076         for(var i=0;i< children.length;i++) {
24077             
24078             this.buttons.add(this.addxtypeChild(children[i]));
24079             
24080         }
24081         
24082         editor.on('editorevent', this.updateToolbar, this);
24083     },
24084     onBtnClick : function(id)
24085     {
24086        this.editorcore.relayCmd(id);
24087        this.editorcore.focus();
24088     },
24089     
24090     /**
24091      * Protected method that will not generally be called directly. It triggers
24092      * a toolbar update by reading the markup state of the current selection in the editor.
24093      */
24094     updateToolbar: function(){
24095
24096         if(!this.editorcore.activated){
24097             this.editor.onFirstFocus(); // is this neeed?
24098             return;
24099         }
24100
24101         var btns = this.buttons; 
24102         var doc = this.editorcore.doc;
24103         btns.get('bold').setActive(doc.queryCommandState('bold'));
24104         btns.get('italic').setActive(doc.queryCommandState('italic'));
24105         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24106         
24107         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24108         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24109         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24110         
24111         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24112         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24113          /*
24114         
24115         var ans = this.editorcore.getAllAncestors();
24116         if (this.formatCombo) {
24117             
24118             
24119             var store = this.formatCombo.store;
24120             this.formatCombo.setValue("");
24121             for (var i =0; i < ans.length;i++) {
24122                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24123                     // select it..
24124                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24125                     break;
24126                 }
24127             }
24128         }
24129         
24130         
24131         
24132         // hides menus... - so this cant be on a menu...
24133         Roo.bootstrap.MenuMgr.hideAll();
24134         */
24135         Roo.bootstrap.MenuMgr.hideAll();
24136         //this.editorsyncValue();
24137     },
24138     onFirstFocus: function() {
24139         this.buttons.each(function(item){
24140            item.enable();
24141         });
24142     },
24143     toggleSourceEdit : function(sourceEditMode){
24144         
24145           
24146         if(sourceEditMode){
24147             Roo.log("disabling buttons");
24148            this.buttons.each( function(item){
24149                 if(item.cmd != 'pencil'){
24150                     item.disable();
24151                 }
24152             });
24153           
24154         }else{
24155             Roo.log("enabling buttons");
24156             if(this.editorcore.initialized){
24157                 this.buttons.each( function(item){
24158                     item.enable();
24159                 });
24160             }
24161             
24162         }
24163         Roo.log("calling toggole on editor");
24164         // tell the editor that it's been pressed..
24165         this.editor.toggleSourceEdit(sourceEditMode);
24166        
24167     }
24168 });
24169
24170
24171
24172
24173
24174 /**
24175  * @class Roo.bootstrap.Table.AbstractSelectionModel
24176  * @extends Roo.util.Observable
24177  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24178  * implemented by descendant classes.  This class should not be directly instantiated.
24179  * @constructor
24180  */
24181 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24182     this.locked = false;
24183     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24184 };
24185
24186
24187 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24188     /** @ignore Called by the grid automatically. Do not call directly. */
24189     init : function(grid){
24190         this.grid = grid;
24191         this.initEvents();
24192     },
24193
24194     /**
24195      * Locks the selections.
24196      */
24197     lock : function(){
24198         this.locked = true;
24199     },
24200
24201     /**
24202      * Unlocks the selections.
24203      */
24204     unlock : function(){
24205         this.locked = false;
24206     },
24207
24208     /**
24209      * Returns true if the selections are locked.
24210      * @return {Boolean}
24211      */
24212     isLocked : function(){
24213         return this.locked;
24214     }
24215 });
24216 /**
24217  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24218  * @class Roo.bootstrap.Table.RowSelectionModel
24219  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24220  * It supports multiple selections and keyboard selection/navigation. 
24221  * @constructor
24222  * @param {Object} config
24223  */
24224
24225 Roo.bootstrap.Table.RowSelectionModel = function(config){
24226     Roo.apply(this, config);
24227     this.selections = new Roo.util.MixedCollection(false, function(o){
24228         return o.id;
24229     });
24230
24231     this.last = false;
24232     this.lastActive = false;
24233
24234     this.addEvents({
24235         /**
24236              * @event selectionchange
24237              * Fires when the selection changes
24238              * @param {SelectionModel} this
24239              */
24240             "selectionchange" : true,
24241         /**
24242              * @event afterselectionchange
24243              * Fires after the selection changes (eg. by key press or clicking)
24244              * @param {SelectionModel} this
24245              */
24246             "afterselectionchange" : true,
24247         /**
24248              * @event beforerowselect
24249              * Fires when a row is selected being selected, return false to cancel.
24250              * @param {SelectionModel} this
24251              * @param {Number} rowIndex The selected index
24252              * @param {Boolean} keepExisting False if other selections will be cleared
24253              */
24254             "beforerowselect" : true,
24255         /**
24256              * @event rowselect
24257              * Fires when a row is selected.
24258              * @param {SelectionModel} this
24259              * @param {Number} rowIndex The selected index
24260              * @param {Roo.data.Record} r The record
24261              */
24262             "rowselect" : true,
24263         /**
24264              * @event rowdeselect
24265              * Fires when a row is deselected.
24266              * @param {SelectionModel} this
24267              * @param {Number} rowIndex The selected index
24268              */
24269         "rowdeselect" : true
24270     });
24271     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24272     this.locked = false;
24273  };
24274
24275 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24276     /**
24277      * @cfg {Boolean} singleSelect
24278      * True to allow selection of only one row at a time (defaults to false)
24279      */
24280     singleSelect : false,
24281
24282     // private
24283     initEvents : function()
24284     {
24285
24286         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24287         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24288         //}else{ // allow click to work like normal
24289          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24290         //}
24291         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24292         this.grid.on("rowclick", this.handleMouseDown, this);
24293         
24294         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24295             "up" : function(e){
24296                 if(!e.shiftKey){
24297                     this.selectPrevious(e.shiftKey);
24298                 }else if(this.last !== false && this.lastActive !== false){
24299                     var last = this.last;
24300                     this.selectRange(this.last,  this.lastActive-1);
24301                     this.grid.getView().focusRow(this.lastActive);
24302                     if(last !== false){
24303                         this.last = last;
24304                     }
24305                 }else{
24306                     this.selectFirstRow();
24307                 }
24308                 this.fireEvent("afterselectionchange", this);
24309             },
24310             "down" : function(e){
24311                 if(!e.shiftKey){
24312                     this.selectNext(e.shiftKey);
24313                 }else if(this.last !== false && this.lastActive !== false){
24314                     var last = this.last;
24315                     this.selectRange(this.last,  this.lastActive+1);
24316                     this.grid.getView().focusRow(this.lastActive);
24317                     if(last !== false){
24318                         this.last = last;
24319                     }
24320                 }else{
24321                     this.selectFirstRow();
24322                 }
24323                 this.fireEvent("afterselectionchange", this);
24324             },
24325             scope: this
24326         });
24327         this.grid.store.on('load', function(){
24328             this.selections.clear();
24329         },this);
24330         /*
24331         var view = this.grid.view;
24332         view.on("refresh", this.onRefresh, this);
24333         view.on("rowupdated", this.onRowUpdated, this);
24334         view.on("rowremoved", this.onRemove, this);
24335         */
24336     },
24337
24338     // private
24339     onRefresh : function()
24340     {
24341         var ds = this.grid.store, i, v = this.grid.view;
24342         var s = this.selections;
24343         s.each(function(r){
24344             if((i = ds.indexOfId(r.id)) != -1){
24345                 v.onRowSelect(i);
24346             }else{
24347                 s.remove(r);
24348             }
24349         });
24350     },
24351
24352     // private
24353     onRemove : function(v, index, r){
24354         this.selections.remove(r);
24355     },
24356
24357     // private
24358     onRowUpdated : function(v, index, r){
24359         if(this.isSelected(r)){
24360             v.onRowSelect(index);
24361         }
24362     },
24363
24364     /**
24365      * Select records.
24366      * @param {Array} records The records to select
24367      * @param {Boolean} keepExisting (optional) True to keep existing selections
24368      */
24369     selectRecords : function(records, keepExisting)
24370     {
24371         if(!keepExisting){
24372             this.clearSelections();
24373         }
24374             var ds = this.grid.store;
24375         for(var i = 0, len = records.length; i < len; i++){
24376             this.selectRow(ds.indexOf(records[i]), true);
24377         }
24378     },
24379
24380     /**
24381      * Gets the number of selected rows.
24382      * @return {Number}
24383      */
24384     getCount : function(){
24385         return this.selections.length;
24386     },
24387
24388     /**
24389      * Selects the first row in the grid.
24390      */
24391     selectFirstRow : function(){
24392         this.selectRow(0);
24393     },
24394
24395     /**
24396      * Select the last row.
24397      * @param {Boolean} keepExisting (optional) True to keep existing selections
24398      */
24399     selectLastRow : function(keepExisting){
24400         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24401         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24402     },
24403
24404     /**
24405      * Selects the row immediately following the last selected row.
24406      * @param {Boolean} keepExisting (optional) True to keep existing selections
24407      */
24408     selectNext : function(keepExisting)
24409     {
24410             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24411             this.selectRow(this.last+1, keepExisting);
24412             this.grid.getView().focusRow(this.last);
24413         }
24414     },
24415
24416     /**
24417      * Selects the row that precedes the last selected row.
24418      * @param {Boolean} keepExisting (optional) True to keep existing selections
24419      */
24420     selectPrevious : function(keepExisting){
24421         if(this.last){
24422             this.selectRow(this.last-1, keepExisting);
24423             this.grid.getView().focusRow(this.last);
24424         }
24425     },
24426
24427     /**
24428      * Returns the selected records
24429      * @return {Array} Array of selected records
24430      */
24431     getSelections : function(){
24432         return [].concat(this.selections.items);
24433     },
24434
24435     /**
24436      * Returns the first selected record.
24437      * @return {Record}
24438      */
24439     getSelected : function(){
24440         return this.selections.itemAt(0);
24441     },
24442
24443
24444     /**
24445      * Clears all selections.
24446      */
24447     clearSelections : function(fast)
24448     {
24449         if(this.locked) {
24450             return;
24451         }
24452         if(fast !== true){
24453                 var ds = this.grid.store;
24454             var s = this.selections;
24455             s.each(function(r){
24456                 this.deselectRow(ds.indexOfId(r.id));
24457             }, this);
24458             s.clear();
24459         }else{
24460             this.selections.clear();
24461         }
24462         this.last = false;
24463     },
24464
24465
24466     /**
24467      * Selects all rows.
24468      */
24469     selectAll : function(){
24470         if(this.locked) {
24471             return;
24472         }
24473         this.selections.clear();
24474         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24475             this.selectRow(i, true);
24476         }
24477     },
24478
24479     /**
24480      * Returns True if there is a selection.
24481      * @return {Boolean}
24482      */
24483     hasSelection : function(){
24484         return this.selections.length > 0;
24485     },
24486
24487     /**
24488      * Returns True if the specified row is selected.
24489      * @param {Number/Record} record The record or index of the record to check
24490      * @return {Boolean}
24491      */
24492     isSelected : function(index){
24493             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24494         return (r && this.selections.key(r.id) ? true : false);
24495     },
24496
24497     /**
24498      * Returns True if the specified record id is selected.
24499      * @param {String} id The id of record to check
24500      * @return {Boolean}
24501      */
24502     isIdSelected : function(id){
24503         return (this.selections.key(id) ? true : false);
24504     },
24505
24506
24507     // private
24508     handleMouseDBClick : function(e, t){
24509         
24510     },
24511     // private
24512     handleMouseDown : function(e, t)
24513     {
24514             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24515         if(this.isLocked() || rowIndex < 0 ){
24516             return;
24517         };
24518         if(e.shiftKey && this.last !== false){
24519             var last = this.last;
24520             this.selectRange(last, rowIndex, e.ctrlKey);
24521             this.last = last; // reset the last
24522             t.focus();
24523     
24524         }else{
24525             var isSelected = this.isSelected(rowIndex);
24526             //Roo.log("select row:" + rowIndex);
24527             if(isSelected){
24528                 this.deselectRow(rowIndex);
24529             } else {
24530                         this.selectRow(rowIndex, true);
24531             }
24532     
24533             /*
24534                 if(e.button !== 0 && isSelected){
24535                 alert('rowIndex 2: ' + rowIndex);
24536                     view.focusRow(rowIndex);
24537                 }else if(e.ctrlKey && isSelected){
24538                     this.deselectRow(rowIndex);
24539                 }else if(!isSelected){
24540                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24541                     view.focusRow(rowIndex);
24542                 }
24543             */
24544         }
24545         this.fireEvent("afterselectionchange", this);
24546     },
24547     // private
24548     handleDragableRowClick :  function(grid, rowIndex, e) 
24549     {
24550         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24551             this.selectRow(rowIndex, false);
24552             grid.view.focusRow(rowIndex);
24553              this.fireEvent("afterselectionchange", this);
24554         }
24555     },
24556     
24557     /**
24558      * Selects multiple rows.
24559      * @param {Array} rows Array of the indexes of the row to select
24560      * @param {Boolean} keepExisting (optional) True to keep existing selections
24561      */
24562     selectRows : function(rows, keepExisting){
24563         if(!keepExisting){
24564             this.clearSelections();
24565         }
24566         for(var i = 0, len = rows.length; i < len; i++){
24567             this.selectRow(rows[i], true);
24568         }
24569     },
24570
24571     /**
24572      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24573      * @param {Number} startRow The index of the first row in the range
24574      * @param {Number} endRow The index of the last row in the range
24575      * @param {Boolean} keepExisting (optional) True to retain existing selections
24576      */
24577     selectRange : function(startRow, endRow, keepExisting){
24578         if(this.locked) {
24579             return;
24580         }
24581         if(!keepExisting){
24582             this.clearSelections();
24583         }
24584         if(startRow <= endRow){
24585             for(var i = startRow; i <= endRow; i++){
24586                 this.selectRow(i, true);
24587             }
24588         }else{
24589             for(var i = startRow; i >= endRow; i--){
24590                 this.selectRow(i, true);
24591             }
24592         }
24593     },
24594
24595     /**
24596      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24597      * @param {Number} startRow The index of the first row in the range
24598      * @param {Number} endRow The index of the last row in the range
24599      */
24600     deselectRange : function(startRow, endRow, preventViewNotify){
24601         if(this.locked) {
24602             return;
24603         }
24604         for(var i = startRow; i <= endRow; i++){
24605             this.deselectRow(i, preventViewNotify);
24606         }
24607     },
24608
24609     /**
24610      * Selects a row.
24611      * @param {Number} row The index of the row to select
24612      * @param {Boolean} keepExisting (optional) True to keep existing selections
24613      */
24614     selectRow : function(index, keepExisting, preventViewNotify)
24615     {
24616             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24617             return;
24618         }
24619         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24620             if(!keepExisting || this.singleSelect){
24621                 this.clearSelections();
24622             }
24623             
24624             var r = this.grid.store.getAt(index);
24625             //console.log('selectRow - record id :' + r.id);
24626             
24627             this.selections.add(r);
24628             this.last = this.lastActive = index;
24629             if(!preventViewNotify){
24630                 var proxy = new Roo.Element(
24631                                 this.grid.getRowDom(index)
24632                 );
24633                 proxy.addClass('bg-info info');
24634             }
24635             this.fireEvent("rowselect", this, index, r);
24636             this.fireEvent("selectionchange", this);
24637         }
24638     },
24639
24640     /**
24641      * Deselects a row.
24642      * @param {Number} row The index of the row to deselect
24643      */
24644     deselectRow : function(index, preventViewNotify)
24645     {
24646         if(this.locked) {
24647             return;
24648         }
24649         if(this.last == index){
24650             this.last = false;
24651         }
24652         if(this.lastActive == index){
24653             this.lastActive = false;
24654         }
24655         
24656         var r = this.grid.store.getAt(index);
24657         if (!r) {
24658             return;
24659         }
24660         
24661         this.selections.remove(r);
24662         //.console.log('deselectRow - record id :' + r.id);
24663         if(!preventViewNotify){
24664         
24665             var proxy = new Roo.Element(
24666                 this.grid.getRowDom(index)
24667             );
24668             proxy.removeClass('bg-info info');
24669         }
24670         this.fireEvent("rowdeselect", this, index);
24671         this.fireEvent("selectionchange", this);
24672     },
24673
24674     // private
24675     restoreLast : function(){
24676         if(this._last){
24677             this.last = this._last;
24678         }
24679     },
24680
24681     // private
24682     acceptsNav : function(row, col, cm){
24683         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24684     },
24685
24686     // private
24687     onEditorKey : function(field, e){
24688         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24689         if(k == e.TAB){
24690             e.stopEvent();
24691             ed.completeEdit();
24692             if(e.shiftKey){
24693                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24694             }else{
24695                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24696             }
24697         }else if(k == e.ENTER && !e.ctrlKey){
24698             e.stopEvent();
24699             ed.completeEdit();
24700             if(e.shiftKey){
24701                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24702             }else{
24703                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24704             }
24705         }else if(k == e.ESC){
24706             ed.cancelEdit();
24707         }
24708         if(newCell){
24709             g.startEditing(newCell[0], newCell[1]);
24710         }
24711     }
24712 });
24713 /*
24714  * Based on:
24715  * Ext JS Library 1.1.1
24716  * Copyright(c) 2006-2007, Ext JS, LLC.
24717  *
24718  * Originally Released Under LGPL - original licence link has changed is not relivant.
24719  *
24720  * Fork - LGPL
24721  * <script type="text/javascript">
24722  */
24723  
24724 /**
24725  * @class Roo.bootstrap.PagingToolbar
24726  * @extends Roo.bootstrap.NavSimplebar
24727  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24728  * @constructor
24729  * Create a new PagingToolbar
24730  * @param {Object} config The config object
24731  * @param {Roo.data.Store} store
24732  */
24733 Roo.bootstrap.PagingToolbar = function(config)
24734 {
24735     // old args format still supported... - xtype is prefered..
24736         // created from xtype...
24737     
24738     this.ds = config.dataSource;
24739     
24740     if (config.store && !this.ds) {
24741         this.store= Roo.factory(config.store, Roo.data);
24742         this.ds = this.store;
24743         this.ds.xmodule = this.xmodule || false;
24744     }
24745     
24746     this.toolbarItems = [];
24747     if (config.items) {
24748         this.toolbarItems = config.items;
24749     }
24750     
24751     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24752     
24753     this.cursor = 0;
24754     
24755     if (this.ds) { 
24756         this.bind(this.ds);
24757     }
24758     
24759     if (Roo.bootstrap.version == 4) {
24760         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24761     } else {
24762         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24763     }
24764     
24765 };
24766
24767 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24768     /**
24769      * @cfg {Roo.data.Store} dataSource
24770      * The underlying data store providing the paged data
24771      */
24772     /**
24773      * @cfg {String/HTMLElement/Element} container
24774      * container The id or element that will contain the toolbar
24775      */
24776     /**
24777      * @cfg {Boolean} displayInfo
24778      * True to display the displayMsg (defaults to false)
24779      */
24780     /**
24781      * @cfg {Number} pageSize
24782      * The number of records to display per page (defaults to 20)
24783      */
24784     pageSize: 20,
24785     /**
24786      * @cfg {String} displayMsg
24787      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24788      */
24789     displayMsg : 'Displaying {0} - {1} of {2}',
24790     /**
24791      * @cfg {String} emptyMsg
24792      * The message to display when no records are found (defaults to "No data to display")
24793      */
24794     emptyMsg : 'No data to display',
24795     /**
24796      * Customizable piece of the default paging text (defaults to "Page")
24797      * @type String
24798      */
24799     beforePageText : "Page",
24800     /**
24801      * Customizable piece of the default paging text (defaults to "of %0")
24802      * @type String
24803      */
24804     afterPageText : "of {0}",
24805     /**
24806      * Customizable piece of the default paging text (defaults to "First Page")
24807      * @type String
24808      */
24809     firstText : "First Page",
24810     /**
24811      * Customizable piece of the default paging text (defaults to "Previous Page")
24812      * @type String
24813      */
24814     prevText : "Previous Page",
24815     /**
24816      * Customizable piece of the default paging text (defaults to "Next Page")
24817      * @type String
24818      */
24819     nextText : "Next Page",
24820     /**
24821      * Customizable piece of the default paging text (defaults to "Last Page")
24822      * @type String
24823      */
24824     lastText : "Last Page",
24825     /**
24826      * Customizable piece of the default paging text (defaults to "Refresh")
24827      * @type String
24828      */
24829     refreshText : "Refresh",
24830
24831     buttons : false,
24832     // private
24833     onRender : function(ct, position) 
24834     {
24835         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24836         this.navgroup.parentId = this.id;
24837         this.navgroup.onRender(this.el, null);
24838         // add the buttons to the navgroup
24839         
24840         if(this.displayInfo){
24841             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24842             this.displayEl = this.el.select('.x-paging-info', true).first();
24843 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24844 //            this.displayEl = navel.el.select('span',true).first();
24845         }
24846         
24847         var _this = this;
24848         
24849         if(this.buttons){
24850             Roo.each(_this.buttons, function(e){ // this might need to use render????
24851                Roo.factory(e).render(_this.el);
24852             });
24853         }
24854             
24855         Roo.each(_this.toolbarItems, function(e) {
24856             _this.navgroup.addItem(e);
24857         });
24858         
24859         
24860         this.first = this.navgroup.addItem({
24861             tooltip: this.firstText,
24862             cls: "prev btn-outline-secondary",
24863             html : ' <i class="fa fa-step-backward"></i>',
24864             disabled: true,
24865             preventDefault: true,
24866             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24867         });
24868         
24869         this.prev =  this.navgroup.addItem({
24870             tooltip: this.prevText,
24871             cls: "prev btn-outline-secondary",
24872             html : ' <i class="fa fa-backward"></i>',
24873             disabled: true,
24874             preventDefault: true,
24875             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24876         });
24877     //this.addSeparator();
24878         
24879         
24880         var field = this.navgroup.addItem( {
24881             tagtype : 'span',
24882             cls : 'x-paging-position  btn-outline-secondary',
24883              disabled: true,
24884             html : this.beforePageText  +
24885                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24886                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24887          } ); //?? escaped?
24888         
24889         this.field = field.el.select('input', true).first();
24890         this.field.on("keydown", this.onPagingKeydown, this);
24891         this.field.on("focus", function(){this.dom.select();});
24892     
24893     
24894         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24895         //this.field.setHeight(18);
24896         //this.addSeparator();
24897         this.next = this.navgroup.addItem({
24898             tooltip: this.nextText,
24899             cls: "next btn-outline-secondary",
24900             html : ' <i class="fa fa-forward"></i>',
24901             disabled: true,
24902             preventDefault: true,
24903             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24904         });
24905         this.last = this.navgroup.addItem({
24906             tooltip: this.lastText,
24907             html : ' <i class="fa fa-step-forward"></i>',
24908             cls: "next btn-outline-secondary",
24909             disabled: true,
24910             preventDefault: true,
24911             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24912         });
24913     //this.addSeparator();
24914         this.loading = this.navgroup.addItem({
24915             tooltip: this.refreshText,
24916             cls: "btn-outline-secondary",
24917             html : ' <i class="fa fa-refresh"></i>',
24918             preventDefault: true,
24919             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24920         });
24921         
24922     },
24923
24924     // private
24925     updateInfo : function(){
24926         if(this.displayEl){
24927             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24928             var msg = count == 0 ?
24929                 this.emptyMsg :
24930                 String.format(
24931                     this.displayMsg,
24932                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24933                 );
24934             this.displayEl.update(msg);
24935         }
24936     },
24937
24938     // private
24939     onLoad : function(ds, r, o)
24940     {
24941         this.cursor = o.params.start ? o.params.start : 0;
24942         
24943         var d = this.getPageData(),
24944             ap = d.activePage,
24945             ps = d.pages;
24946         
24947         
24948         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24949         this.field.dom.value = ap;
24950         this.first.setDisabled(ap == 1);
24951         this.prev.setDisabled(ap == 1);
24952         this.next.setDisabled(ap == ps);
24953         this.last.setDisabled(ap == ps);
24954         this.loading.enable();
24955         this.updateInfo();
24956     },
24957
24958     // private
24959     getPageData : function(){
24960         var total = this.ds.getTotalCount();
24961         return {
24962             total : total,
24963             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24964             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24965         };
24966     },
24967
24968     // private
24969     onLoadError : function(){
24970         this.loading.enable();
24971     },
24972
24973     // private
24974     onPagingKeydown : function(e){
24975         var k = e.getKey();
24976         var d = this.getPageData();
24977         if(k == e.RETURN){
24978             var v = this.field.dom.value, pageNum;
24979             if(!v || isNaN(pageNum = parseInt(v, 10))){
24980                 this.field.dom.value = d.activePage;
24981                 return;
24982             }
24983             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24984             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24985             e.stopEvent();
24986         }
24987         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))
24988         {
24989           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24990           this.field.dom.value = pageNum;
24991           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24992           e.stopEvent();
24993         }
24994         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24995         {
24996           var v = this.field.dom.value, pageNum; 
24997           var increment = (e.shiftKey) ? 10 : 1;
24998           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24999                 increment *= -1;
25000           }
25001           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25002             this.field.dom.value = d.activePage;
25003             return;
25004           }
25005           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25006           {
25007             this.field.dom.value = parseInt(v, 10) + increment;
25008             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25009             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25010           }
25011           e.stopEvent();
25012         }
25013     },
25014
25015     // private
25016     beforeLoad : function(){
25017         if(this.loading){
25018             this.loading.disable();
25019         }
25020     },
25021
25022     // private
25023     onClick : function(which){
25024         
25025         var ds = this.ds;
25026         if (!ds) {
25027             return;
25028         }
25029         
25030         switch(which){
25031             case "first":
25032                 ds.load({params:{start: 0, limit: this.pageSize}});
25033             break;
25034             case "prev":
25035                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25036             break;
25037             case "next":
25038                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25039             break;
25040             case "last":
25041                 var total = ds.getTotalCount();
25042                 var extra = total % this.pageSize;
25043                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25044                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25045             break;
25046             case "refresh":
25047                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25048             break;
25049         }
25050     },
25051
25052     /**
25053      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25054      * @param {Roo.data.Store} store The data store to unbind
25055      */
25056     unbind : function(ds){
25057         ds.un("beforeload", this.beforeLoad, this);
25058         ds.un("load", this.onLoad, this);
25059         ds.un("loadexception", this.onLoadError, this);
25060         ds.un("remove", this.updateInfo, this);
25061         ds.un("add", this.updateInfo, this);
25062         this.ds = undefined;
25063     },
25064
25065     /**
25066      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25067      * @param {Roo.data.Store} store The data store to bind
25068      */
25069     bind : function(ds){
25070         ds.on("beforeload", this.beforeLoad, this);
25071         ds.on("load", this.onLoad, this);
25072         ds.on("loadexception", this.onLoadError, this);
25073         ds.on("remove", this.updateInfo, this);
25074         ds.on("add", this.updateInfo, this);
25075         this.ds = ds;
25076     }
25077 });/*
25078  * - LGPL
25079  *
25080  * element
25081  * 
25082  */
25083
25084 /**
25085  * @class Roo.bootstrap.MessageBar
25086  * @extends Roo.bootstrap.Component
25087  * Bootstrap MessageBar class
25088  * @cfg {String} html contents of the MessageBar
25089  * @cfg {String} weight (info | success | warning | danger) default info
25090  * @cfg {String} beforeClass insert the bar before the given class
25091  * @cfg {Boolean} closable (true | false) default false
25092  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25093  * 
25094  * @constructor
25095  * Create a new Element
25096  * @param {Object} config The config object
25097  */
25098
25099 Roo.bootstrap.MessageBar = function(config){
25100     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25101 };
25102
25103 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25104     
25105     html: '',
25106     weight: 'info',
25107     closable: false,
25108     fixed: false,
25109     beforeClass: 'bootstrap-sticky-wrap',
25110     
25111     getAutoCreate : function(){
25112         
25113         var cfg = {
25114             tag: 'div',
25115             cls: 'alert alert-dismissable alert-' + this.weight,
25116             cn: [
25117                 {
25118                     tag: 'span',
25119                     cls: 'message',
25120                     html: this.html || ''
25121                 }
25122             ]
25123         };
25124         
25125         if(this.fixed){
25126             cfg.cls += ' alert-messages-fixed';
25127         }
25128         
25129         if(this.closable){
25130             cfg.cn.push({
25131                 tag: 'button',
25132                 cls: 'close',
25133                 html: 'x'
25134             });
25135         }
25136         
25137         return cfg;
25138     },
25139     
25140     onRender : function(ct, position)
25141     {
25142         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25143         
25144         if(!this.el){
25145             var cfg = Roo.apply({},  this.getAutoCreate());
25146             cfg.id = Roo.id();
25147             
25148             if (this.cls) {
25149                 cfg.cls += ' ' + this.cls;
25150             }
25151             if (this.style) {
25152                 cfg.style = this.style;
25153             }
25154             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25155             
25156             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25157         }
25158         
25159         this.el.select('>button.close').on('click', this.hide, this);
25160         
25161     },
25162     
25163     show : function()
25164     {
25165         if (!this.rendered) {
25166             this.render();
25167         }
25168         
25169         this.el.show();
25170         
25171         this.fireEvent('show', this);
25172         
25173     },
25174     
25175     hide : function()
25176     {
25177         if (!this.rendered) {
25178             this.render();
25179         }
25180         
25181         this.el.hide();
25182         
25183         this.fireEvent('hide', this);
25184     },
25185     
25186     update : function()
25187     {
25188 //        var e = this.el.dom.firstChild;
25189 //        
25190 //        if(this.closable){
25191 //            e = e.nextSibling;
25192 //        }
25193 //        
25194 //        e.data = this.html || '';
25195
25196         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25197     }
25198    
25199 });
25200
25201  
25202
25203      /*
25204  * - LGPL
25205  *
25206  * Graph
25207  * 
25208  */
25209
25210
25211 /**
25212  * @class Roo.bootstrap.Graph
25213  * @extends Roo.bootstrap.Component
25214  * Bootstrap Graph class
25215 > Prameters
25216  -sm {number} sm 4
25217  -md {number} md 5
25218  @cfg {String} graphtype  bar | vbar | pie
25219  @cfg {number} g_x coodinator | centre x (pie)
25220  @cfg {number} g_y coodinator | centre y (pie)
25221  @cfg {number} g_r radius (pie)
25222  @cfg {number} g_height height of the chart (respected by all elements in the set)
25223  @cfg {number} g_width width of the chart (respected by all elements in the set)
25224  @cfg {Object} title The title of the chart
25225     
25226  -{Array}  values
25227  -opts (object) options for the chart 
25228      o {
25229      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25230      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25231      o vgutter (number)
25232      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.
25233      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25234      o to
25235      o stretch (boolean)
25236      o }
25237  -opts (object) options for the pie
25238      o{
25239      o cut
25240      o startAngle (number)
25241      o endAngle (number)
25242      } 
25243  *
25244  * @constructor
25245  * Create a new Input
25246  * @param {Object} config The config object
25247  */
25248
25249 Roo.bootstrap.Graph = function(config){
25250     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25251     
25252     this.addEvents({
25253         // img events
25254         /**
25255          * @event click
25256          * The img click event for the img.
25257          * @param {Roo.EventObject} e
25258          */
25259         "click" : true
25260     });
25261 };
25262
25263 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25264     
25265     sm: 4,
25266     md: 5,
25267     graphtype: 'bar',
25268     g_height: 250,
25269     g_width: 400,
25270     g_x: 50,
25271     g_y: 50,
25272     g_r: 30,
25273     opts:{
25274         //g_colors: this.colors,
25275         g_type: 'soft',
25276         g_gutter: '20%'
25277
25278     },
25279     title : false,
25280
25281     getAutoCreate : function(){
25282         
25283         var cfg = {
25284             tag: 'div',
25285             html : null
25286         };
25287         
25288         
25289         return  cfg;
25290     },
25291
25292     onRender : function(ct,position){
25293         
25294         
25295         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25296         
25297         if (typeof(Raphael) == 'undefined') {
25298             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25299             return;
25300         }
25301         
25302         this.raphael = Raphael(this.el.dom);
25303         
25304                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25305                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25306                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25307                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25308                 /*
25309                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25310                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25311                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25312                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25313                 
25314                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25315                 r.barchart(330, 10, 300, 220, data1);
25316                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25317                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25318                 */
25319                 
25320                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25321                 // r.barchart(30, 30, 560, 250,  xdata, {
25322                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25323                 //     axis : "0 0 1 1",
25324                 //     axisxlabels :  xdata
25325                 //     //yvalues : cols,
25326                    
25327                 // });
25328 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25329 //        
25330 //        this.load(null,xdata,{
25331 //                axis : "0 0 1 1",
25332 //                axisxlabels :  xdata
25333 //                });
25334
25335     },
25336
25337     load : function(graphtype,xdata,opts)
25338     {
25339         this.raphael.clear();
25340         if(!graphtype) {
25341             graphtype = this.graphtype;
25342         }
25343         if(!opts){
25344             opts = this.opts;
25345         }
25346         var r = this.raphael,
25347             fin = function () {
25348                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25349             },
25350             fout = function () {
25351                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25352             },
25353             pfin = function() {
25354                 this.sector.stop();
25355                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25356
25357                 if (this.label) {
25358                     this.label[0].stop();
25359                     this.label[0].attr({ r: 7.5 });
25360                     this.label[1].attr({ "font-weight": 800 });
25361                 }
25362             },
25363             pfout = function() {
25364                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25365
25366                 if (this.label) {
25367                     this.label[0].animate({ r: 5 }, 500, "bounce");
25368                     this.label[1].attr({ "font-weight": 400 });
25369                 }
25370             };
25371
25372         switch(graphtype){
25373             case 'bar':
25374                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25375                 break;
25376             case 'hbar':
25377                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25378                 break;
25379             case 'pie':
25380 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25381 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25382 //            
25383                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25384                 
25385                 break;
25386
25387         }
25388         
25389         if(this.title){
25390             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25391         }
25392         
25393     },
25394     
25395     setTitle: function(o)
25396     {
25397         this.title = o;
25398     },
25399     
25400     initEvents: function() {
25401         
25402         if(!this.href){
25403             this.el.on('click', this.onClick, this);
25404         }
25405     },
25406     
25407     onClick : function(e)
25408     {
25409         Roo.log('img onclick');
25410         this.fireEvent('click', this, e);
25411     }
25412    
25413 });
25414
25415  
25416 /*
25417  * - LGPL
25418  *
25419  * numberBox
25420  * 
25421  */
25422 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25423
25424 /**
25425  * @class Roo.bootstrap.dash.NumberBox
25426  * @extends Roo.bootstrap.Component
25427  * Bootstrap NumberBox class
25428  * @cfg {String} headline Box headline
25429  * @cfg {String} content Box content
25430  * @cfg {String} icon Box icon
25431  * @cfg {String} footer Footer text
25432  * @cfg {String} fhref Footer href
25433  * 
25434  * @constructor
25435  * Create a new NumberBox
25436  * @param {Object} config The config object
25437  */
25438
25439
25440 Roo.bootstrap.dash.NumberBox = function(config){
25441     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25442     
25443 };
25444
25445 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25446     
25447     headline : '',
25448     content : '',
25449     icon : '',
25450     footer : '',
25451     fhref : '',
25452     ficon : '',
25453     
25454     getAutoCreate : function(){
25455         
25456         var cfg = {
25457             tag : 'div',
25458             cls : 'small-box ',
25459             cn : [
25460                 {
25461                     tag : 'div',
25462                     cls : 'inner',
25463                     cn :[
25464                         {
25465                             tag : 'h3',
25466                             cls : 'roo-headline',
25467                             html : this.headline
25468                         },
25469                         {
25470                             tag : 'p',
25471                             cls : 'roo-content',
25472                             html : this.content
25473                         }
25474                     ]
25475                 }
25476             ]
25477         };
25478         
25479         if(this.icon){
25480             cfg.cn.push({
25481                 tag : 'div',
25482                 cls : 'icon',
25483                 cn :[
25484                     {
25485                         tag : 'i',
25486                         cls : 'ion ' + this.icon
25487                     }
25488                 ]
25489             });
25490         }
25491         
25492         if(this.footer){
25493             var footer = {
25494                 tag : 'a',
25495                 cls : 'small-box-footer',
25496                 href : this.fhref || '#',
25497                 html : this.footer
25498             };
25499             
25500             cfg.cn.push(footer);
25501             
25502         }
25503         
25504         return  cfg;
25505     },
25506
25507     onRender : function(ct,position){
25508         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25509
25510
25511        
25512                 
25513     },
25514
25515     setHeadline: function (value)
25516     {
25517         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25518     },
25519     
25520     setFooter: function (value, href)
25521     {
25522         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25523         
25524         if(href){
25525             this.el.select('a.small-box-footer',true).first().attr('href', href);
25526         }
25527         
25528     },
25529
25530     setContent: function (value)
25531     {
25532         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25533     },
25534
25535     initEvents: function() 
25536     {   
25537         
25538     }
25539     
25540 });
25541
25542  
25543 /*
25544  * - LGPL
25545  *
25546  * TabBox
25547  * 
25548  */
25549 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25550
25551 /**
25552  * @class Roo.bootstrap.dash.TabBox
25553  * @extends Roo.bootstrap.Component
25554  * Bootstrap TabBox class
25555  * @cfg {String} title Title of the TabBox
25556  * @cfg {String} icon Icon of the TabBox
25557  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25558  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25559  * 
25560  * @constructor
25561  * Create a new TabBox
25562  * @param {Object} config The config object
25563  */
25564
25565
25566 Roo.bootstrap.dash.TabBox = function(config){
25567     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25568     this.addEvents({
25569         // raw events
25570         /**
25571          * @event addpane
25572          * When a pane is added
25573          * @param {Roo.bootstrap.dash.TabPane} pane
25574          */
25575         "addpane" : true,
25576         /**
25577          * @event activatepane
25578          * When a pane is activated
25579          * @param {Roo.bootstrap.dash.TabPane} pane
25580          */
25581         "activatepane" : true
25582         
25583          
25584     });
25585     
25586     this.panes = [];
25587 };
25588
25589 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25590
25591     title : '',
25592     icon : false,
25593     showtabs : true,
25594     tabScrollable : false,
25595     
25596     getChildContainer : function()
25597     {
25598         return this.el.select('.tab-content', true).first();
25599     },
25600     
25601     getAutoCreate : function(){
25602         
25603         var header = {
25604             tag: 'li',
25605             cls: 'pull-left header',
25606             html: this.title,
25607             cn : []
25608         };
25609         
25610         if(this.icon){
25611             header.cn.push({
25612                 tag: 'i',
25613                 cls: 'fa ' + this.icon
25614             });
25615         }
25616         
25617         var h = {
25618             tag: 'ul',
25619             cls: 'nav nav-tabs pull-right',
25620             cn: [
25621                 header
25622             ]
25623         };
25624         
25625         if(this.tabScrollable){
25626             h = {
25627                 tag: 'div',
25628                 cls: 'tab-header',
25629                 cn: [
25630                     {
25631                         tag: 'ul',
25632                         cls: 'nav nav-tabs pull-right',
25633                         cn: [
25634                             header
25635                         ]
25636                     }
25637                 ]
25638             };
25639         }
25640         
25641         var cfg = {
25642             tag: 'div',
25643             cls: 'nav-tabs-custom',
25644             cn: [
25645                 h,
25646                 {
25647                     tag: 'div',
25648                     cls: 'tab-content no-padding',
25649                     cn: []
25650                 }
25651             ]
25652         };
25653
25654         return  cfg;
25655     },
25656     initEvents : function()
25657     {
25658         //Roo.log('add add pane handler');
25659         this.on('addpane', this.onAddPane, this);
25660     },
25661      /**
25662      * Updates the box title
25663      * @param {String} html to set the title to.
25664      */
25665     setTitle : function(value)
25666     {
25667         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25668     },
25669     onAddPane : function(pane)
25670     {
25671         this.panes.push(pane);
25672         //Roo.log('addpane');
25673         //Roo.log(pane);
25674         // tabs are rendere left to right..
25675         if(!this.showtabs){
25676             return;
25677         }
25678         
25679         var ctr = this.el.select('.nav-tabs', true).first();
25680          
25681          
25682         var existing = ctr.select('.nav-tab',true);
25683         var qty = existing.getCount();;
25684         
25685         
25686         var tab = ctr.createChild({
25687             tag : 'li',
25688             cls : 'nav-tab' + (qty ? '' : ' active'),
25689             cn : [
25690                 {
25691                     tag : 'a',
25692                     href:'#',
25693                     html : pane.title
25694                 }
25695             ]
25696         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25697         pane.tab = tab;
25698         
25699         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25700         if (!qty) {
25701             pane.el.addClass('active');
25702         }
25703         
25704                 
25705     },
25706     onTabClick : function(ev,un,ob,pane)
25707     {
25708         //Roo.log('tab - prev default');
25709         ev.preventDefault();
25710         
25711         
25712         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25713         pane.tab.addClass('active');
25714         //Roo.log(pane.title);
25715         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25716         // technically we should have a deactivate event.. but maybe add later.
25717         // and it should not de-activate the selected tab...
25718         this.fireEvent('activatepane', pane);
25719         pane.el.addClass('active');
25720         pane.fireEvent('activate');
25721         
25722         
25723     },
25724     
25725     getActivePane : function()
25726     {
25727         var r = false;
25728         Roo.each(this.panes, function(p) {
25729             if(p.el.hasClass('active')){
25730                 r = p;
25731                 return false;
25732             }
25733             
25734             return;
25735         });
25736         
25737         return r;
25738     }
25739     
25740     
25741 });
25742
25743  
25744 /*
25745  * - LGPL
25746  *
25747  * Tab pane
25748  * 
25749  */
25750 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25751 /**
25752  * @class Roo.bootstrap.TabPane
25753  * @extends Roo.bootstrap.Component
25754  * Bootstrap TabPane class
25755  * @cfg {Boolean} active (false | true) Default false
25756  * @cfg {String} title title of panel
25757
25758  * 
25759  * @constructor
25760  * Create a new TabPane
25761  * @param {Object} config The config object
25762  */
25763
25764 Roo.bootstrap.dash.TabPane = function(config){
25765     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25766     
25767     this.addEvents({
25768         // raw events
25769         /**
25770          * @event activate
25771          * When a pane is activated
25772          * @param {Roo.bootstrap.dash.TabPane} pane
25773          */
25774         "activate" : true
25775          
25776     });
25777 };
25778
25779 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25780     
25781     active : false,
25782     title : '',
25783     
25784     // the tabBox that this is attached to.
25785     tab : false,
25786      
25787     getAutoCreate : function() 
25788     {
25789         var cfg = {
25790             tag: 'div',
25791             cls: 'tab-pane'
25792         };
25793         
25794         if(this.active){
25795             cfg.cls += ' active';
25796         }
25797         
25798         return cfg;
25799     },
25800     initEvents  : function()
25801     {
25802         //Roo.log('trigger add pane handler');
25803         this.parent().fireEvent('addpane', this)
25804     },
25805     
25806      /**
25807      * Updates the tab title 
25808      * @param {String} html to set the title to.
25809      */
25810     setTitle: function(str)
25811     {
25812         if (!this.tab) {
25813             return;
25814         }
25815         this.title = str;
25816         this.tab.select('a', true).first().dom.innerHTML = str;
25817         
25818     }
25819     
25820     
25821     
25822 });
25823
25824  
25825
25826
25827  /*
25828  * - LGPL
25829  *
25830  * menu
25831  * 
25832  */
25833 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25834
25835 /**
25836  * @class Roo.bootstrap.menu.Menu
25837  * @extends Roo.bootstrap.Component
25838  * Bootstrap Menu class - container for Menu
25839  * @cfg {String} html Text of the menu
25840  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25841  * @cfg {String} icon Font awesome icon
25842  * @cfg {String} pos Menu align to (top | bottom) default bottom
25843  * 
25844  * 
25845  * @constructor
25846  * Create a new Menu
25847  * @param {Object} config The config object
25848  */
25849
25850
25851 Roo.bootstrap.menu.Menu = function(config){
25852     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25853     
25854     this.addEvents({
25855         /**
25856          * @event beforeshow
25857          * Fires before this menu is displayed
25858          * @param {Roo.bootstrap.menu.Menu} this
25859          */
25860         beforeshow : true,
25861         /**
25862          * @event beforehide
25863          * Fires before this menu is hidden
25864          * @param {Roo.bootstrap.menu.Menu} this
25865          */
25866         beforehide : true,
25867         /**
25868          * @event show
25869          * Fires after this menu is displayed
25870          * @param {Roo.bootstrap.menu.Menu} this
25871          */
25872         show : true,
25873         /**
25874          * @event hide
25875          * Fires after this menu is hidden
25876          * @param {Roo.bootstrap.menu.Menu} this
25877          */
25878         hide : true,
25879         /**
25880          * @event click
25881          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25882          * @param {Roo.bootstrap.menu.Menu} this
25883          * @param {Roo.EventObject} e
25884          */
25885         click : true
25886     });
25887     
25888 };
25889
25890 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25891     
25892     submenu : false,
25893     html : '',
25894     weight : 'default',
25895     icon : false,
25896     pos : 'bottom',
25897     
25898     
25899     getChildContainer : function() {
25900         if(this.isSubMenu){
25901             return this.el;
25902         }
25903         
25904         return this.el.select('ul.dropdown-menu', true).first();  
25905     },
25906     
25907     getAutoCreate : function()
25908     {
25909         var text = [
25910             {
25911                 tag : 'span',
25912                 cls : 'roo-menu-text',
25913                 html : this.html
25914             }
25915         ];
25916         
25917         if(this.icon){
25918             text.unshift({
25919                 tag : 'i',
25920                 cls : 'fa ' + this.icon
25921             })
25922         }
25923         
25924         
25925         var cfg = {
25926             tag : 'div',
25927             cls : 'btn-group',
25928             cn : [
25929                 {
25930                     tag : 'button',
25931                     cls : 'dropdown-button btn btn-' + this.weight,
25932                     cn : text
25933                 },
25934                 {
25935                     tag : 'button',
25936                     cls : 'dropdown-toggle btn btn-' + this.weight,
25937                     cn : [
25938                         {
25939                             tag : 'span',
25940                             cls : 'caret'
25941                         }
25942                     ]
25943                 },
25944                 {
25945                     tag : 'ul',
25946                     cls : 'dropdown-menu'
25947                 }
25948             ]
25949             
25950         };
25951         
25952         if(this.pos == 'top'){
25953             cfg.cls += ' dropup';
25954         }
25955         
25956         if(this.isSubMenu){
25957             cfg = {
25958                 tag : 'ul',
25959                 cls : 'dropdown-menu'
25960             }
25961         }
25962         
25963         return cfg;
25964     },
25965     
25966     onRender : function(ct, position)
25967     {
25968         this.isSubMenu = ct.hasClass('dropdown-submenu');
25969         
25970         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25971     },
25972     
25973     initEvents : function() 
25974     {
25975         if(this.isSubMenu){
25976             return;
25977         }
25978         
25979         this.hidden = true;
25980         
25981         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25982         this.triggerEl.on('click', this.onTriggerPress, this);
25983         
25984         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25985         this.buttonEl.on('click', this.onClick, this);
25986         
25987     },
25988     
25989     list : function()
25990     {
25991         if(this.isSubMenu){
25992             return this.el;
25993         }
25994         
25995         return this.el.select('ul.dropdown-menu', true).first();
25996     },
25997     
25998     onClick : function(e)
25999     {
26000         this.fireEvent("click", this, e);
26001     },
26002     
26003     onTriggerPress  : function(e)
26004     {   
26005         if (this.isVisible()) {
26006             this.hide();
26007         } else {
26008             this.show();
26009         }
26010     },
26011     
26012     isVisible : function(){
26013         return !this.hidden;
26014     },
26015     
26016     show : function()
26017     {
26018         this.fireEvent("beforeshow", this);
26019         
26020         this.hidden = false;
26021         this.el.addClass('open');
26022         
26023         Roo.get(document).on("mouseup", this.onMouseUp, this);
26024         
26025         this.fireEvent("show", this);
26026         
26027         
26028     },
26029     
26030     hide : function()
26031     {
26032         this.fireEvent("beforehide", this);
26033         
26034         this.hidden = true;
26035         this.el.removeClass('open');
26036         
26037         Roo.get(document).un("mouseup", this.onMouseUp);
26038         
26039         this.fireEvent("hide", this);
26040     },
26041     
26042     onMouseUp : function()
26043     {
26044         this.hide();
26045     }
26046     
26047 });
26048
26049  
26050  /*
26051  * - LGPL
26052  *
26053  * menu item
26054  * 
26055  */
26056 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26057
26058 /**
26059  * @class Roo.bootstrap.menu.Item
26060  * @extends Roo.bootstrap.Component
26061  * Bootstrap MenuItem class
26062  * @cfg {Boolean} submenu (true | false) default false
26063  * @cfg {String} html text of the item
26064  * @cfg {String} href the link
26065  * @cfg {Boolean} disable (true | false) default false
26066  * @cfg {Boolean} preventDefault (true | false) default true
26067  * @cfg {String} icon Font awesome icon
26068  * @cfg {String} pos Submenu align to (left | right) default right 
26069  * 
26070  * 
26071  * @constructor
26072  * Create a new Item
26073  * @param {Object} config The config object
26074  */
26075
26076
26077 Roo.bootstrap.menu.Item = function(config){
26078     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26079     this.addEvents({
26080         /**
26081          * @event mouseover
26082          * Fires when the mouse is hovering over this menu
26083          * @param {Roo.bootstrap.menu.Item} this
26084          * @param {Roo.EventObject} e
26085          */
26086         mouseover : true,
26087         /**
26088          * @event mouseout
26089          * Fires when the mouse exits this menu
26090          * @param {Roo.bootstrap.menu.Item} this
26091          * @param {Roo.EventObject} e
26092          */
26093         mouseout : true,
26094         // raw events
26095         /**
26096          * @event click
26097          * The raw click event for the entire grid.
26098          * @param {Roo.EventObject} e
26099          */
26100         click : true
26101     });
26102 };
26103
26104 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26105     
26106     submenu : false,
26107     href : '',
26108     html : '',
26109     preventDefault: true,
26110     disable : false,
26111     icon : false,
26112     pos : 'right',
26113     
26114     getAutoCreate : function()
26115     {
26116         var text = [
26117             {
26118                 tag : 'span',
26119                 cls : 'roo-menu-item-text',
26120                 html : this.html
26121             }
26122         ];
26123         
26124         if(this.icon){
26125             text.unshift({
26126                 tag : 'i',
26127                 cls : 'fa ' + this.icon
26128             })
26129         }
26130         
26131         var cfg = {
26132             tag : 'li',
26133             cn : [
26134                 {
26135                     tag : 'a',
26136                     href : this.href || '#',
26137                     cn : text
26138                 }
26139             ]
26140         };
26141         
26142         if(this.disable){
26143             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26144         }
26145         
26146         if(this.submenu){
26147             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26148             
26149             if(this.pos == 'left'){
26150                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26151             }
26152         }
26153         
26154         return cfg;
26155     },
26156     
26157     initEvents : function() 
26158     {
26159         this.el.on('mouseover', this.onMouseOver, this);
26160         this.el.on('mouseout', this.onMouseOut, this);
26161         
26162         this.el.select('a', true).first().on('click', this.onClick, this);
26163         
26164     },
26165     
26166     onClick : function(e)
26167     {
26168         if(this.preventDefault){
26169             e.preventDefault();
26170         }
26171         
26172         this.fireEvent("click", this, e);
26173     },
26174     
26175     onMouseOver : function(e)
26176     {
26177         if(this.submenu && this.pos == 'left'){
26178             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26179         }
26180         
26181         this.fireEvent("mouseover", this, e);
26182     },
26183     
26184     onMouseOut : function(e)
26185     {
26186         this.fireEvent("mouseout", this, e);
26187     }
26188 });
26189
26190  
26191
26192  /*
26193  * - LGPL
26194  *
26195  * menu separator
26196  * 
26197  */
26198 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26199
26200 /**
26201  * @class Roo.bootstrap.menu.Separator
26202  * @extends Roo.bootstrap.Component
26203  * Bootstrap Separator class
26204  * 
26205  * @constructor
26206  * Create a new Separator
26207  * @param {Object} config The config object
26208  */
26209
26210
26211 Roo.bootstrap.menu.Separator = function(config){
26212     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26213 };
26214
26215 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26216     
26217     getAutoCreate : function(){
26218         var cfg = {
26219             tag : 'li',
26220             cls: 'divider'
26221         };
26222         
26223         return cfg;
26224     }
26225    
26226 });
26227
26228  
26229
26230  /*
26231  * - LGPL
26232  *
26233  * Tooltip
26234  * 
26235  */
26236
26237 /**
26238  * @class Roo.bootstrap.Tooltip
26239  * Bootstrap Tooltip class
26240  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26241  * to determine which dom element triggers the tooltip.
26242  * 
26243  * It needs to add support for additional attributes like tooltip-position
26244  * 
26245  * @constructor
26246  * Create a new Toolti
26247  * @param {Object} config The config object
26248  */
26249
26250 Roo.bootstrap.Tooltip = function(config){
26251     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26252     
26253     this.alignment = Roo.bootstrap.Tooltip.alignment;
26254     
26255     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26256         this.alignment = config.alignment;
26257     }
26258     
26259 };
26260
26261 Roo.apply(Roo.bootstrap.Tooltip, {
26262     /**
26263      * @function init initialize tooltip monitoring.
26264      * @static
26265      */
26266     currentEl : false,
26267     currentTip : false,
26268     currentRegion : false,
26269     
26270     //  init : delay?
26271     
26272     init : function()
26273     {
26274         Roo.get(document).on('mouseover', this.enter ,this);
26275         Roo.get(document).on('mouseout', this.leave, this);
26276          
26277         
26278         this.currentTip = new Roo.bootstrap.Tooltip();
26279     },
26280     
26281     enter : function(ev)
26282     {
26283         var dom = ev.getTarget();
26284         
26285         //Roo.log(['enter',dom]);
26286         var el = Roo.fly(dom);
26287         if (this.currentEl) {
26288             //Roo.log(dom);
26289             //Roo.log(this.currentEl);
26290             //Roo.log(this.currentEl.contains(dom));
26291             if (this.currentEl == el) {
26292                 return;
26293             }
26294             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26295                 return;
26296             }
26297
26298         }
26299         
26300         if (this.currentTip.el) {
26301             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26302         }    
26303         //Roo.log(ev);
26304         
26305         if(!el || el.dom == document){
26306             return;
26307         }
26308         
26309         var bindEl = el;
26310         
26311         // you can not look for children, as if el is the body.. then everythign is the child..
26312         if (!el.attr('tooltip')) { //
26313             if (!el.select("[tooltip]").elements.length) {
26314                 return;
26315             }
26316             // is the mouse over this child...?
26317             bindEl = el.select("[tooltip]").first();
26318             var xy = ev.getXY();
26319             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26320                 //Roo.log("not in region.");
26321                 return;
26322             }
26323             //Roo.log("child element over..");
26324             
26325         }
26326         this.currentEl = bindEl;
26327         this.currentTip.bind(bindEl);
26328         this.currentRegion = Roo.lib.Region.getRegion(dom);
26329         this.currentTip.enter();
26330         
26331     },
26332     leave : function(ev)
26333     {
26334         var dom = ev.getTarget();
26335         //Roo.log(['leave',dom]);
26336         if (!this.currentEl) {
26337             return;
26338         }
26339         
26340         
26341         if (dom != this.currentEl.dom) {
26342             return;
26343         }
26344         var xy = ev.getXY();
26345         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26346             return;
26347         }
26348         // only activate leave if mouse cursor is outside... bounding box..
26349         
26350         
26351         
26352         
26353         if (this.currentTip) {
26354             this.currentTip.leave();
26355         }
26356         //Roo.log('clear currentEl');
26357         this.currentEl = false;
26358         
26359         
26360     },
26361     alignment : {
26362         'left' : ['r-l', [-2,0], 'right'],
26363         'right' : ['l-r', [2,0], 'left'],
26364         'bottom' : ['t-b', [0,2], 'top'],
26365         'top' : [ 'b-t', [0,-2], 'bottom']
26366     }
26367     
26368 });
26369
26370
26371 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26372     
26373     
26374     bindEl : false,
26375     
26376     delay : null, // can be { show : 300 , hide: 500}
26377     
26378     timeout : null,
26379     
26380     hoverState : null, //???
26381     
26382     placement : 'bottom', 
26383     
26384     alignment : false,
26385     
26386     getAutoCreate : function(){
26387     
26388         var cfg = {
26389            cls : 'tooltip',
26390            role : 'tooltip',
26391            cn : [
26392                 {
26393                     cls : 'tooltip-arrow'
26394                 },
26395                 {
26396                     cls : 'tooltip-inner'
26397                 }
26398            ]
26399         };
26400         
26401         return cfg;
26402     },
26403     bind : function(el)
26404     {
26405         this.bindEl = el;
26406     },
26407       
26408     
26409     enter : function () {
26410        
26411         if (this.timeout != null) {
26412             clearTimeout(this.timeout);
26413         }
26414         
26415         this.hoverState = 'in';
26416          //Roo.log("enter - show");
26417         if (!this.delay || !this.delay.show) {
26418             this.show();
26419             return;
26420         }
26421         var _t = this;
26422         this.timeout = setTimeout(function () {
26423             if (_t.hoverState == 'in') {
26424                 _t.show();
26425             }
26426         }, this.delay.show);
26427     },
26428     leave : function()
26429     {
26430         clearTimeout(this.timeout);
26431     
26432         this.hoverState = 'out';
26433          if (!this.delay || !this.delay.hide) {
26434             this.hide();
26435             return;
26436         }
26437        
26438         var _t = this;
26439         this.timeout = setTimeout(function () {
26440             //Roo.log("leave - timeout");
26441             
26442             if (_t.hoverState == 'out') {
26443                 _t.hide();
26444                 Roo.bootstrap.Tooltip.currentEl = false;
26445             }
26446         }, delay);
26447     },
26448     
26449     show : function (msg)
26450     {
26451         if (!this.el) {
26452             this.render(document.body);
26453         }
26454         // set content.
26455         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26456         
26457         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26458         
26459         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26460         
26461         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26462         
26463         var placement = typeof this.placement == 'function' ?
26464             this.placement.call(this, this.el, on_el) :
26465             this.placement;
26466             
26467         var autoToken = /\s?auto?\s?/i;
26468         var autoPlace = autoToken.test(placement);
26469         if (autoPlace) {
26470             placement = placement.replace(autoToken, '') || 'top';
26471         }
26472         
26473         //this.el.detach()
26474         //this.el.setXY([0,0]);
26475         this.el.show();
26476         //this.el.dom.style.display='block';
26477         
26478         //this.el.appendTo(on_el);
26479         
26480         var p = this.getPosition();
26481         var box = this.el.getBox();
26482         
26483         if (autoPlace) {
26484             // fixme..
26485         }
26486         
26487         var align = this.alignment[placement];
26488         
26489         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26490         
26491         if(placement == 'top' || placement == 'bottom'){
26492             if(xy[0] < 0){
26493                 placement = 'right';
26494             }
26495             
26496             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26497                 placement = 'left';
26498             }
26499             
26500             var scroll = Roo.select('body', true).first().getScroll();
26501             
26502             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26503                 placement = 'top';
26504             }
26505             
26506             align = this.alignment[placement];
26507         }
26508         
26509         this.el.alignTo(this.bindEl, align[0],align[1]);
26510         //var arrow = this.el.select('.arrow',true).first();
26511         //arrow.set(align[2], 
26512         
26513         this.el.addClass(placement);
26514         
26515         this.el.addClass('in fade');
26516         
26517         this.hoverState = null;
26518         
26519         if (this.el.hasClass('fade')) {
26520             // fade it?
26521         }
26522         
26523     },
26524     hide : function()
26525     {
26526          
26527         if (!this.el) {
26528             return;
26529         }
26530         //this.el.setXY([0,0]);
26531         this.el.removeClass('in');
26532         //this.el.hide();
26533         
26534     }
26535     
26536 });
26537  
26538
26539  /*
26540  * - LGPL
26541  *
26542  * Location Picker
26543  * 
26544  */
26545
26546 /**
26547  * @class Roo.bootstrap.LocationPicker
26548  * @extends Roo.bootstrap.Component
26549  * Bootstrap LocationPicker class
26550  * @cfg {Number} latitude Position when init default 0
26551  * @cfg {Number} longitude Position when init default 0
26552  * @cfg {Number} zoom default 15
26553  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26554  * @cfg {Boolean} mapTypeControl default false
26555  * @cfg {Boolean} disableDoubleClickZoom default false
26556  * @cfg {Boolean} scrollwheel default true
26557  * @cfg {Boolean} streetViewControl default false
26558  * @cfg {Number} radius default 0
26559  * @cfg {String} locationName
26560  * @cfg {Boolean} draggable default true
26561  * @cfg {Boolean} enableAutocomplete default false
26562  * @cfg {Boolean} enableReverseGeocode default true
26563  * @cfg {String} markerTitle
26564  * 
26565  * @constructor
26566  * Create a new LocationPicker
26567  * @param {Object} config The config object
26568  */
26569
26570
26571 Roo.bootstrap.LocationPicker = function(config){
26572     
26573     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26574     
26575     this.addEvents({
26576         /**
26577          * @event initial
26578          * Fires when the picker initialized.
26579          * @param {Roo.bootstrap.LocationPicker} this
26580          * @param {Google Location} location
26581          */
26582         initial : true,
26583         /**
26584          * @event positionchanged
26585          * Fires when the picker position changed.
26586          * @param {Roo.bootstrap.LocationPicker} this
26587          * @param {Google Location} location
26588          */
26589         positionchanged : true,
26590         /**
26591          * @event resize
26592          * Fires when the map resize.
26593          * @param {Roo.bootstrap.LocationPicker} this
26594          */
26595         resize : true,
26596         /**
26597          * @event show
26598          * Fires when the map show.
26599          * @param {Roo.bootstrap.LocationPicker} this
26600          */
26601         show : true,
26602         /**
26603          * @event hide
26604          * Fires when the map hide.
26605          * @param {Roo.bootstrap.LocationPicker} this
26606          */
26607         hide : true,
26608         /**
26609          * @event mapClick
26610          * Fires when click the map.
26611          * @param {Roo.bootstrap.LocationPicker} this
26612          * @param {Map event} e
26613          */
26614         mapClick : true,
26615         /**
26616          * @event mapRightClick
26617          * Fires when right click the map.
26618          * @param {Roo.bootstrap.LocationPicker} this
26619          * @param {Map event} e
26620          */
26621         mapRightClick : true,
26622         /**
26623          * @event markerClick
26624          * Fires when click the marker.
26625          * @param {Roo.bootstrap.LocationPicker} this
26626          * @param {Map event} e
26627          */
26628         markerClick : true,
26629         /**
26630          * @event markerRightClick
26631          * Fires when right click the marker.
26632          * @param {Roo.bootstrap.LocationPicker} this
26633          * @param {Map event} e
26634          */
26635         markerRightClick : true,
26636         /**
26637          * @event OverlayViewDraw
26638          * Fires when OverlayView Draw
26639          * @param {Roo.bootstrap.LocationPicker} this
26640          */
26641         OverlayViewDraw : true,
26642         /**
26643          * @event OverlayViewOnAdd
26644          * Fires when OverlayView Draw
26645          * @param {Roo.bootstrap.LocationPicker} this
26646          */
26647         OverlayViewOnAdd : true,
26648         /**
26649          * @event OverlayViewOnRemove
26650          * Fires when OverlayView Draw
26651          * @param {Roo.bootstrap.LocationPicker} this
26652          */
26653         OverlayViewOnRemove : true,
26654         /**
26655          * @event OverlayViewShow
26656          * Fires when OverlayView Draw
26657          * @param {Roo.bootstrap.LocationPicker} this
26658          * @param {Pixel} cpx
26659          */
26660         OverlayViewShow : true,
26661         /**
26662          * @event OverlayViewHide
26663          * Fires when OverlayView Draw
26664          * @param {Roo.bootstrap.LocationPicker} this
26665          */
26666         OverlayViewHide : true,
26667         /**
26668          * @event loadexception
26669          * Fires when load google lib failed.
26670          * @param {Roo.bootstrap.LocationPicker} this
26671          */
26672         loadexception : true
26673     });
26674         
26675 };
26676
26677 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26678     
26679     gMapContext: false,
26680     
26681     latitude: 0,
26682     longitude: 0,
26683     zoom: 15,
26684     mapTypeId: false,
26685     mapTypeControl: false,
26686     disableDoubleClickZoom: false,
26687     scrollwheel: true,
26688     streetViewControl: false,
26689     radius: 0,
26690     locationName: '',
26691     draggable: true,
26692     enableAutocomplete: false,
26693     enableReverseGeocode: true,
26694     markerTitle: '',
26695     
26696     getAutoCreate: function()
26697     {
26698
26699         var cfg = {
26700             tag: 'div',
26701             cls: 'roo-location-picker'
26702         };
26703         
26704         return cfg
26705     },
26706     
26707     initEvents: function(ct, position)
26708     {       
26709         if(!this.el.getWidth() || this.isApplied()){
26710             return;
26711         }
26712         
26713         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26714         
26715         this.initial();
26716     },
26717     
26718     initial: function()
26719     {
26720         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26721             this.fireEvent('loadexception', this);
26722             return;
26723         }
26724         
26725         if(!this.mapTypeId){
26726             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26727         }
26728         
26729         this.gMapContext = this.GMapContext();
26730         
26731         this.initOverlayView();
26732         
26733         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26734         
26735         var _this = this;
26736                 
26737         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26738             _this.setPosition(_this.gMapContext.marker.position);
26739         });
26740         
26741         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26742             _this.fireEvent('mapClick', this, event);
26743             
26744         });
26745
26746         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26747             _this.fireEvent('mapRightClick', this, event);
26748             
26749         });
26750         
26751         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26752             _this.fireEvent('markerClick', this, event);
26753             
26754         });
26755
26756         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26757             _this.fireEvent('markerRightClick', this, event);
26758             
26759         });
26760         
26761         this.setPosition(this.gMapContext.location);
26762         
26763         this.fireEvent('initial', this, this.gMapContext.location);
26764     },
26765     
26766     initOverlayView: function()
26767     {
26768         var _this = this;
26769         
26770         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26771             
26772             draw: function()
26773             {
26774                 _this.fireEvent('OverlayViewDraw', _this);
26775             },
26776             
26777             onAdd: function()
26778             {
26779                 _this.fireEvent('OverlayViewOnAdd', _this);
26780             },
26781             
26782             onRemove: function()
26783             {
26784                 _this.fireEvent('OverlayViewOnRemove', _this);
26785             },
26786             
26787             show: function(cpx)
26788             {
26789                 _this.fireEvent('OverlayViewShow', _this, cpx);
26790             },
26791             
26792             hide: function()
26793             {
26794                 _this.fireEvent('OverlayViewHide', _this);
26795             }
26796             
26797         });
26798     },
26799     
26800     fromLatLngToContainerPixel: function(event)
26801     {
26802         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26803     },
26804     
26805     isApplied: function() 
26806     {
26807         return this.getGmapContext() == false ? false : true;
26808     },
26809     
26810     getGmapContext: function() 
26811     {
26812         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26813     },
26814     
26815     GMapContext: function() 
26816     {
26817         var position = new google.maps.LatLng(this.latitude, this.longitude);
26818         
26819         var _map = new google.maps.Map(this.el.dom, {
26820             center: position,
26821             zoom: this.zoom,
26822             mapTypeId: this.mapTypeId,
26823             mapTypeControl: this.mapTypeControl,
26824             disableDoubleClickZoom: this.disableDoubleClickZoom,
26825             scrollwheel: this.scrollwheel,
26826             streetViewControl: this.streetViewControl,
26827             locationName: this.locationName,
26828             draggable: this.draggable,
26829             enableAutocomplete: this.enableAutocomplete,
26830             enableReverseGeocode: this.enableReverseGeocode
26831         });
26832         
26833         var _marker = new google.maps.Marker({
26834             position: position,
26835             map: _map,
26836             title: this.markerTitle,
26837             draggable: this.draggable
26838         });
26839         
26840         return {
26841             map: _map,
26842             marker: _marker,
26843             circle: null,
26844             location: position,
26845             radius: this.radius,
26846             locationName: this.locationName,
26847             addressComponents: {
26848                 formatted_address: null,
26849                 addressLine1: null,
26850                 addressLine2: null,
26851                 streetName: null,
26852                 streetNumber: null,
26853                 city: null,
26854                 district: null,
26855                 state: null,
26856                 stateOrProvince: null
26857             },
26858             settings: this,
26859             domContainer: this.el.dom,
26860             geodecoder: new google.maps.Geocoder()
26861         };
26862     },
26863     
26864     drawCircle: function(center, radius, options) 
26865     {
26866         if (this.gMapContext.circle != null) {
26867             this.gMapContext.circle.setMap(null);
26868         }
26869         if (radius > 0) {
26870             radius *= 1;
26871             options = Roo.apply({}, options, {
26872                 strokeColor: "#0000FF",
26873                 strokeOpacity: .35,
26874                 strokeWeight: 2,
26875                 fillColor: "#0000FF",
26876                 fillOpacity: .2
26877             });
26878             
26879             options.map = this.gMapContext.map;
26880             options.radius = radius;
26881             options.center = center;
26882             this.gMapContext.circle = new google.maps.Circle(options);
26883             return this.gMapContext.circle;
26884         }
26885         
26886         return null;
26887     },
26888     
26889     setPosition: function(location) 
26890     {
26891         this.gMapContext.location = location;
26892         this.gMapContext.marker.setPosition(location);
26893         this.gMapContext.map.panTo(location);
26894         this.drawCircle(location, this.gMapContext.radius, {});
26895         
26896         var _this = this;
26897         
26898         if (this.gMapContext.settings.enableReverseGeocode) {
26899             this.gMapContext.geodecoder.geocode({
26900                 latLng: this.gMapContext.location
26901             }, function(results, status) {
26902                 
26903                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26904                     _this.gMapContext.locationName = results[0].formatted_address;
26905                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26906                     
26907                     _this.fireEvent('positionchanged', this, location);
26908                 }
26909             });
26910             
26911             return;
26912         }
26913         
26914         this.fireEvent('positionchanged', this, location);
26915     },
26916     
26917     resize: function()
26918     {
26919         google.maps.event.trigger(this.gMapContext.map, "resize");
26920         
26921         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26922         
26923         this.fireEvent('resize', this);
26924     },
26925     
26926     setPositionByLatLng: function(latitude, longitude)
26927     {
26928         this.setPosition(new google.maps.LatLng(latitude, longitude));
26929     },
26930     
26931     getCurrentPosition: function() 
26932     {
26933         return {
26934             latitude: this.gMapContext.location.lat(),
26935             longitude: this.gMapContext.location.lng()
26936         };
26937     },
26938     
26939     getAddressName: function() 
26940     {
26941         return this.gMapContext.locationName;
26942     },
26943     
26944     getAddressComponents: function() 
26945     {
26946         return this.gMapContext.addressComponents;
26947     },
26948     
26949     address_component_from_google_geocode: function(address_components) 
26950     {
26951         var result = {};
26952         
26953         for (var i = 0; i < address_components.length; i++) {
26954             var component = address_components[i];
26955             if (component.types.indexOf("postal_code") >= 0) {
26956                 result.postalCode = component.short_name;
26957             } else if (component.types.indexOf("street_number") >= 0) {
26958                 result.streetNumber = component.short_name;
26959             } else if (component.types.indexOf("route") >= 0) {
26960                 result.streetName = component.short_name;
26961             } else if (component.types.indexOf("neighborhood") >= 0) {
26962                 result.city = component.short_name;
26963             } else if (component.types.indexOf("locality") >= 0) {
26964                 result.city = component.short_name;
26965             } else if (component.types.indexOf("sublocality") >= 0) {
26966                 result.district = component.short_name;
26967             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26968                 result.stateOrProvince = component.short_name;
26969             } else if (component.types.indexOf("country") >= 0) {
26970                 result.country = component.short_name;
26971             }
26972         }
26973         
26974         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26975         result.addressLine2 = "";
26976         return result;
26977     },
26978     
26979     setZoomLevel: function(zoom)
26980     {
26981         this.gMapContext.map.setZoom(zoom);
26982     },
26983     
26984     show: function()
26985     {
26986         if(!this.el){
26987             return;
26988         }
26989         
26990         this.el.show();
26991         
26992         this.resize();
26993         
26994         this.fireEvent('show', this);
26995     },
26996     
26997     hide: function()
26998     {
26999         if(!this.el){
27000             return;
27001         }
27002         
27003         this.el.hide();
27004         
27005         this.fireEvent('hide', this);
27006     }
27007     
27008 });
27009
27010 Roo.apply(Roo.bootstrap.LocationPicker, {
27011     
27012     OverlayView : function(map, options)
27013     {
27014         options = options || {};
27015         
27016         this.setMap(map);
27017     }
27018     
27019     
27020 });/*
27021  * - LGPL
27022  *
27023  * Alert
27024  * 
27025  */
27026
27027 /**
27028  * @class Roo.bootstrap.Alert
27029  * @extends Roo.bootstrap.Component
27030  * Bootstrap Alert class
27031  * @cfg {String} title The title of alert
27032  * @cfg {String} html The content of alert
27033  * @cfg {String} weight (  success | info | warning | danger )
27034  * @cfg {String} faicon font-awesomeicon
27035  * 
27036  * @constructor
27037  * Create a new alert
27038  * @param {Object} config The config object
27039  */
27040
27041
27042 Roo.bootstrap.Alert = function(config){
27043     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27044     
27045 };
27046
27047 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27048     
27049     title: '',
27050     html: '',
27051     weight: false,
27052     faicon: false,
27053     
27054     getAutoCreate : function()
27055     {
27056         
27057         var cfg = {
27058             tag : 'div',
27059             cls : 'alert',
27060             cn : [
27061                 {
27062                     tag : 'i',
27063                     cls : 'roo-alert-icon'
27064                     
27065                 },
27066                 {
27067                     tag : 'b',
27068                     cls : 'roo-alert-title',
27069                     html : this.title
27070                 },
27071                 {
27072                     tag : 'span',
27073                     cls : 'roo-alert-text',
27074                     html : this.html
27075                 }
27076             ]
27077         };
27078         
27079         if(this.faicon){
27080             cfg.cn[0].cls += ' fa ' + this.faicon;
27081         }
27082         
27083         if(this.weight){
27084             cfg.cls += ' alert-' + this.weight;
27085         }
27086         
27087         return cfg;
27088     },
27089     
27090     initEvents: function() 
27091     {
27092         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27093     },
27094     
27095     setTitle : function(str)
27096     {
27097         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27098     },
27099     
27100     setText : function(str)
27101     {
27102         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27103     },
27104     
27105     setWeight : function(weight)
27106     {
27107         if(this.weight){
27108             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27109         }
27110         
27111         this.weight = weight;
27112         
27113         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27114     },
27115     
27116     setIcon : function(icon)
27117     {
27118         if(this.faicon){
27119             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27120         }
27121         
27122         this.faicon = icon;
27123         
27124         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27125     },
27126     
27127     hide: function() 
27128     {
27129         this.el.hide();   
27130     },
27131     
27132     show: function() 
27133     {  
27134         this.el.show();   
27135     }
27136     
27137 });
27138
27139  
27140 /*
27141 * Licence: LGPL
27142 */
27143
27144 /**
27145  * @class Roo.bootstrap.UploadCropbox
27146  * @extends Roo.bootstrap.Component
27147  * Bootstrap UploadCropbox class
27148  * @cfg {String} emptyText show when image has been loaded
27149  * @cfg {String} rotateNotify show when image too small to rotate
27150  * @cfg {Number} errorTimeout default 3000
27151  * @cfg {Number} minWidth default 300
27152  * @cfg {Number} minHeight default 300
27153  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27154  * @cfg {Boolean} isDocument (true|false) default false
27155  * @cfg {String} url action url
27156  * @cfg {String} paramName default 'imageUpload'
27157  * @cfg {String} method default POST
27158  * @cfg {Boolean} loadMask (true|false) default true
27159  * @cfg {Boolean} loadingText default 'Loading...'
27160  * 
27161  * @constructor
27162  * Create a new UploadCropbox
27163  * @param {Object} config The config object
27164  */
27165
27166 Roo.bootstrap.UploadCropbox = function(config){
27167     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27168     
27169     this.addEvents({
27170         /**
27171          * @event beforeselectfile
27172          * Fire before select file
27173          * @param {Roo.bootstrap.UploadCropbox} this
27174          */
27175         "beforeselectfile" : true,
27176         /**
27177          * @event initial
27178          * Fire after initEvent
27179          * @param {Roo.bootstrap.UploadCropbox} this
27180          */
27181         "initial" : true,
27182         /**
27183          * @event crop
27184          * Fire after initEvent
27185          * @param {Roo.bootstrap.UploadCropbox} this
27186          * @param {String} data
27187          */
27188         "crop" : true,
27189         /**
27190          * @event prepare
27191          * Fire when preparing the file data
27192          * @param {Roo.bootstrap.UploadCropbox} this
27193          * @param {Object} file
27194          */
27195         "prepare" : true,
27196         /**
27197          * @event exception
27198          * Fire when get exception
27199          * @param {Roo.bootstrap.UploadCropbox} this
27200          * @param {XMLHttpRequest} xhr
27201          */
27202         "exception" : true,
27203         /**
27204          * @event beforeloadcanvas
27205          * Fire before load the canvas
27206          * @param {Roo.bootstrap.UploadCropbox} this
27207          * @param {String} src
27208          */
27209         "beforeloadcanvas" : true,
27210         /**
27211          * @event trash
27212          * Fire when trash image
27213          * @param {Roo.bootstrap.UploadCropbox} this
27214          */
27215         "trash" : true,
27216         /**
27217          * @event download
27218          * Fire when download the image
27219          * @param {Roo.bootstrap.UploadCropbox} this
27220          */
27221         "download" : true,
27222         /**
27223          * @event footerbuttonclick
27224          * Fire when footerbuttonclick
27225          * @param {Roo.bootstrap.UploadCropbox} this
27226          * @param {String} type
27227          */
27228         "footerbuttonclick" : true,
27229         /**
27230          * @event resize
27231          * Fire when resize
27232          * @param {Roo.bootstrap.UploadCropbox} this
27233          */
27234         "resize" : true,
27235         /**
27236          * @event rotate
27237          * Fire when rotate the image
27238          * @param {Roo.bootstrap.UploadCropbox} this
27239          * @param {String} pos
27240          */
27241         "rotate" : true,
27242         /**
27243          * @event inspect
27244          * Fire when inspect the file
27245          * @param {Roo.bootstrap.UploadCropbox} this
27246          * @param {Object} file
27247          */
27248         "inspect" : true,
27249         /**
27250          * @event upload
27251          * Fire when xhr upload the file
27252          * @param {Roo.bootstrap.UploadCropbox} this
27253          * @param {Object} data
27254          */
27255         "upload" : true,
27256         /**
27257          * @event arrange
27258          * Fire when arrange the file data
27259          * @param {Roo.bootstrap.UploadCropbox} this
27260          * @param {Object} formData
27261          */
27262         "arrange" : true
27263     });
27264     
27265     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27266 };
27267
27268 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27269     
27270     emptyText : 'Click to upload image',
27271     rotateNotify : 'Image is too small to rotate',
27272     errorTimeout : 3000,
27273     scale : 0,
27274     baseScale : 1,
27275     rotate : 0,
27276     dragable : false,
27277     pinching : false,
27278     mouseX : 0,
27279     mouseY : 0,
27280     cropData : false,
27281     minWidth : 300,
27282     minHeight : 300,
27283     file : false,
27284     exif : {},
27285     baseRotate : 1,
27286     cropType : 'image/jpeg',
27287     buttons : false,
27288     canvasLoaded : false,
27289     isDocument : false,
27290     method : 'POST',
27291     paramName : 'imageUpload',
27292     loadMask : true,
27293     loadingText : 'Loading...',
27294     maskEl : false,
27295     
27296     getAutoCreate : function()
27297     {
27298         var cfg = {
27299             tag : 'div',
27300             cls : 'roo-upload-cropbox',
27301             cn : [
27302                 {
27303                     tag : 'input',
27304                     cls : 'roo-upload-cropbox-selector',
27305                     type : 'file'
27306                 },
27307                 {
27308                     tag : 'div',
27309                     cls : 'roo-upload-cropbox-body',
27310                     style : 'cursor:pointer',
27311                     cn : [
27312                         {
27313                             tag : 'div',
27314                             cls : 'roo-upload-cropbox-preview'
27315                         },
27316                         {
27317                             tag : 'div',
27318                             cls : 'roo-upload-cropbox-thumb'
27319                         },
27320                         {
27321                             tag : 'div',
27322                             cls : 'roo-upload-cropbox-empty-notify',
27323                             html : this.emptyText
27324                         },
27325                         {
27326                             tag : 'div',
27327                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27328                             html : this.rotateNotify
27329                         }
27330                     ]
27331                 },
27332                 {
27333                     tag : 'div',
27334                     cls : 'roo-upload-cropbox-footer',
27335                     cn : {
27336                         tag : 'div',
27337                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27338                         cn : []
27339                     }
27340                 }
27341             ]
27342         };
27343         
27344         return cfg;
27345     },
27346     
27347     onRender : function(ct, position)
27348     {
27349         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27350         
27351         if (this.buttons.length) {
27352             
27353             Roo.each(this.buttons, function(bb) {
27354                 
27355                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27356                 
27357                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27358                 
27359             }, this);
27360         }
27361         
27362         if(this.loadMask){
27363             this.maskEl = this.el;
27364         }
27365     },
27366     
27367     initEvents : function()
27368     {
27369         this.urlAPI = (window.createObjectURL && window) || 
27370                                 (window.URL && URL.revokeObjectURL && URL) || 
27371                                 (window.webkitURL && webkitURL);
27372                         
27373         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27374         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27375         
27376         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27377         this.selectorEl.hide();
27378         
27379         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27380         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27381         
27382         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27383         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27384         this.thumbEl.hide();
27385         
27386         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27387         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27388         
27389         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27390         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27391         this.errorEl.hide();
27392         
27393         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27394         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27395         this.footerEl.hide();
27396         
27397         this.setThumbBoxSize();
27398         
27399         this.bind();
27400         
27401         this.resize();
27402         
27403         this.fireEvent('initial', this);
27404     },
27405
27406     bind : function()
27407     {
27408         var _this = this;
27409         
27410         window.addEventListener("resize", function() { _this.resize(); } );
27411         
27412         this.bodyEl.on('click', this.beforeSelectFile, this);
27413         
27414         if(Roo.isTouch){
27415             this.bodyEl.on('touchstart', this.onTouchStart, this);
27416             this.bodyEl.on('touchmove', this.onTouchMove, this);
27417             this.bodyEl.on('touchend', this.onTouchEnd, this);
27418         }
27419         
27420         if(!Roo.isTouch){
27421             this.bodyEl.on('mousedown', this.onMouseDown, this);
27422             this.bodyEl.on('mousemove', this.onMouseMove, this);
27423             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27424             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27425             Roo.get(document).on('mouseup', this.onMouseUp, this);
27426         }
27427         
27428         this.selectorEl.on('change', this.onFileSelected, this);
27429     },
27430     
27431     reset : function()
27432     {    
27433         this.scale = 0;
27434         this.baseScale = 1;
27435         this.rotate = 0;
27436         this.baseRotate = 1;
27437         this.dragable = false;
27438         this.pinching = false;
27439         this.mouseX = 0;
27440         this.mouseY = 0;
27441         this.cropData = false;
27442         this.notifyEl.dom.innerHTML = this.emptyText;
27443         
27444         this.selectorEl.dom.value = '';
27445         
27446     },
27447     
27448     resize : function()
27449     {
27450         if(this.fireEvent('resize', this) != false){
27451             this.setThumbBoxPosition();
27452             this.setCanvasPosition();
27453         }
27454     },
27455     
27456     onFooterButtonClick : function(e, el, o, type)
27457     {
27458         switch (type) {
27459             case 'rotate-left' :
27460                 this.onRotateLeft(e);
27461                 break;
27462             case 'rotate-right' :
27463                 this.onRotateRight(e);
27464                 break;
27465             case 'picture' :
27466                 this.beforeSelectFile(e);
27467                 break;
27468             case 'trash' :
27469                 this.trash(e);
27470                 break;
27471             case 'crop' :
27472                 this.crop(e);
27473                 break;
27474             case 'download' :
27475                 this.download(e);
27476                 break;
27477             default :
27478                 break;
27479         }
27480         
27481         this.fireEvent('footerbuttonclick', this, type);
27482     },
27483     
27484     beforeSelectFile : function(e)
27485     {
27486         e.preventDefault();
27487         
27488         if(this.fireEvent('beforeselectfile', this) != false){
27489             this.selectorEl.dom.click();
27490         }
27491     },
27492     
27493     onFileSelected : function(e)
27494     {
27495         e.preventDefault();
27496         
27497         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27498             return;
27499         }
27500         
27501         var file = this.selectorEl.dom.files[0];
27502         
27503         if(this.fireEvent('inspect', this, file) != false){
27504             this.prepare(file);
27505         }
27506         
27507     },
27508     
27509     trash : function(e)
27510     {
27511         this.fireEvent('trash', this);
27512     },
27513     
27514     download : function(e)
27515     {
27516         this.fireEvent('download', this);
27517     },
27518     
27519     loadCanvas : function(src)
27520     {   
27521         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27522             
27523             this.reset();
27524             
27525             this.imageEl = document.createElement('img');
27526             
27527             var _this = this;
27528             
27529             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27530             
27531             this.imageEl.src = src;
27532         }
27533     },
27534     
27535     onLoadCanvas : function()
27536     {   
27537         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27538         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27539         
27540         this.bodyEl.un('click', this.beforeSelectFile, this);
27541         
27542         this.notifyEl.hide();
27543         this.thumbEl.show();
27544         this.footerEl.show();
27545         
27546         this.baseRotateLevel();
27547         
27548         if(this.isDocument){
27549             this.setThumbBoxSize();
27550         }
27551         
27552         this.setThumbBoxPosition();
27553         
27554         this.baseScaleLevel();
27555         
27556         this.draw();
27557         
27558         this.resize();
27559         
27560         this.canvasLoaded = true;
27561         
27562         if(this.loadMask){
27563             this.maskEl.unmask();
27564         }
27565         
27566     },
27567     
27568     setCanvasPosition : function()
27569     {   
27570         if(!this.canvasEl){
27571             return;
27572         }
27573         
27574         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27575         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27576         
27577         this.previewEl.setLeft(pw);
27578         this.previewEl.setTop(ph);
27579         
27580     },
27581     
27582     onMouseDown : function(e)
27583     {   
27584         e.stopEvent();
27585         
27586         this.dragable = true;
27587         this.pinching = false;
27588         
27589         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27590             this.dragable = false;
27591             return;
27592         }
27593         
27594         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27595         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27596         
27597     },
27598     
27599     onMouseMove : function(e)
27600     {   
27601         e.stopEvent();
27602         
27603         if(!this.canvasLoaded){
27604             return;
27605         }
27606         
27607         if (!this.dragable){
27608             return;
27609         }
27610         
27611         var minX = Math.ceil(this.thumbEl.getLeft(true));
27612         var minY = Math.ceil(this.thumbEl.getTop(true));
27613         
27614         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27615         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27616         
27617         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27618         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27619         
27620         x = x - this.mouseX;
27621         y = y - this.mouseY;
27622         
27623         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27624         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27625         
27626         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27627         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27628         
27629         this.previewEl.setLeft(bgX);
27630         this.previewEl.setTop(bgY);
27631         
27632         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27633         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27634     },
27635     
27636     onMouseUp : function(e)
27637     {   
27638         e.stopEvent();
27639         
27640         this.dragable = false;
27641     },
27642     
27643     onMouseWheel : function(e)
27644     {   
27645         e.stopEvent();
27646         
27647         this.startScale = this.scale;
27648         
27649         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27650         
27651         if(!this.zoomable()){
27652             this.scale = this.startScale;
27653             return;
27654         }
27655         
27656         this.draw();
27657         
27658         return;
27659     },
27660     
27661     zoomable : function()
27662     {
27663         var minScale = this.thumbEl.getWidth() / this.minWidth;
27664         
27665         if(this.minWidth < this.minHeight){
27666             minScale = this.thumbEl.getHeight() / this.minHeight;
27667         }
27668         
27669         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27670         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27671         
27672         if(
27673                 this.isDocument &&
27674                 (this.rotate == 0 || this.rotate == 180) && 
27675                 (
27676                     width > this.imageEl.OriginWidth || 
27677                     height > this.imageEl.OriginHeight ||
27678                     (width < this.minWidth && height < this.minHeight)
27679                 )
27680         ){
27681             return false;
27682         }
27683         
27684         if(
27685                 this.isDocument &&
27686                 (this.rotate == 90 || this.rotate == 270) && 
27687                 (
27688                     width > this.imageEl.OriginWidth || 
27689                     height > this.imageEl.OriginHeight ||
27690                     (width < this.minHeight && height < this.minWidth)
27691                 )
27692         ){
27693             return false;
27694         }
27695         
27696         if(
27697                 !this.isDocument &&
27698                 (this.rotate == 0 || this.rotate == 180) && 
27699                 (
27700                     width < this.minWidth || 
27701                     width > this.imageEl.OriginWidth || 
27702                     height < this.minHeight || 
27703                     height > this.imageEl.OriginHeight
27704                 )
27705         ){
27706             return false;
27707         }
27708         
27709         if(
27710                 !this.isDocument &&
27711                 (this.rotate == 90 || this.rotate == 270) && 
27712                 (
27713                     width < this.minHeight || 
27714                     width > this.imageEl.OriginWidth || 
27715                     height < this.minWidth || 
27716                     height > this.imageEl.OriginHeight
27717                 )
27718         ){
27719             return false;
27720         }
27721         
27722         return true;
27723         
27724     },
27725     
27726     onRotateLeft : function(e)
27727     {   
27728         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27729             
27730             var minScale = this.thumbEl.getWidth() / this.minWidth;
27731             
27732             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27733             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27734             
27735             this.startScale = this.scale;
27736             
27737             while (this.getScaleLevel() < minScale){
27738             
27739                 this.scale = this.scale + 1;
27740                 
27741                 if(!this.zoomable()){
27742                     break;
27743                 }
27744                 
27745                 if(
27746                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27747                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27748                 ){
27749                     continue;
27750                 }
27751                 
27752                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27753
27754                 this.draw();
27755                 
27756                 return;
27757             }
27758             
27759             this.scale = this.startScale;
27760             
27761             this.onRotateFail();
27762             
27763             return false;
27764         }
27765         
27766         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27767
27768         if(this.isDocument){
27769             this.setThumbBoxSize();
27770             this.setThumbBoxPosition();
27771             this.setCanvasPosition();
27772         }
27773         
27774         this.draw();
27775         
27776         this.fireEvent('rotate', this, 'left');
27777         
27778     },
27779     
27780     onRotateRight : function(e)
27781     {
27782         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27783             
27784             var minScale = this.thumbEl.getWidth() / this.minWidth;
27785         
27786             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27787             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27788             
27789             this.startScale = this.scale;
27790             
27791             while (this.getScaleLevel() < minScale){
27792             
27793                 this.scale = this.scale + 1;
27794                 
27795                 if(!this.zoomable()){
27796                     break;
27797                 }
27798                 
27799                 if(
27800                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27801                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27802                 ){
27803                     continue;
27804                 }
27805                 
27806                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27807
27808                 this.draw();
27809                 
27810                 return;
27811             }
27812             
27813             this.scale = this.startScale;
27814             
27815             this.onRotateFail();
27816             
27817             return false;
27818         }
27819         
27820         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27821
27822         if(this.isDocument){
27823             this.setThumbBoxSize();
27824             this.setThumbBoxPosition();
27825             this.setCanvasPosition();
27826         }
27827         
27828         this.draw();
27829         
27830         this.fireEvent('rotate', this, 'right');
27831     },
27832     
27833     onRotateFail : function()
27834     {
27835         this.errorEl.show(true);
27836         
27837         var _this = this;
27838         
27839         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27840     },
27841     
27842     draw : function()
27843     {
27844         this.previewEl.dom.innerHTML = '';
27845         
27846         var canvasEl = document.createElement("canvas");
27847         
27848         var contextEl = canvasEl.getContext("2d");
27849         
27850         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27851         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27852         var center = this.imageEl.OriginWidth / 2;
27853         
27854         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27855             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27856             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27857             center = this.imageEl.OriginHeight / 2;
27858         }
27859         
27860         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27861         
27862         contextEl.translate(center, center);
27863         contextEl.rotate(this.rotate * Math.PI / 180);
27864
27865         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27866         
27867         this.canvasEl = document.createElement("canvas");
27868         
27869         this.contextEl = this.canvasEl.getContext("2d");
27870         
27871         switch (this.rotate) {
27872             case 0 :
27873                 
27874                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27875                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27876                 
27877                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27878                 
27879                 break;
27880             case 90 : 
27881                 
27882                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27883                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27884                 
27885                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27886                     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);
27887                     break;
27888                 }
27889                 
27890                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27891                 
27892                 break;
27893             case 180 :
27894                 
27895                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27896                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27897                 
27898                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27899                     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);
27900                     break;
27901                 }
27902                 
27903                 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);
27904                 
27905                 break;
27906             case 270 :
27907                 
27908                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27909                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27910         
27911                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27912                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27913                     break;
27914                 }
27915                 
27916                 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);
27917                 
27918                 break;
27919             default : 
27920                 break;
27921         }
27922         
27923         this.previewEl.appendChild(this.canvasEl);
27924         
27925         this.setCanvasPosition();
27926     },
27927     
27928     crop : function()
27929     {
27930         if(!this.canvasLoaded){
27931             return;
27932         }
27933         
27934         var imageCanvas = document.createElement("canvas");
27935         
27936         var imageContext = imageCanvas.getContext("2d");
27937         
27938         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27939         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27940         
27941         var center = imageCanvas.width / 2;
27942         
27943         imageContext.translate(center, center);
27944         
27945         imageContext.rotate(this.rotate * Math.PI / 180);
27946         
27947         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27948         
27949         var canvas = document.createElement("canvas");
27950         
27951         var context = canvas.getContext("2d");
27952                 
27953         canvas.width = this.minWidth;
27954         canvas.height = this.minHeight;
27955
27956         switch (this.rotate) {
27957             case 0 :
27958                 
27959                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27960                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27961                 
27962                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27963                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27964                 
27965                 var targetWidth = this.minWidth - 2 * x;
27966                 var targetHeight = this.minHeight - 2 * y;
27967                 
27968                 var scale = 1;
27969                 
27970                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27971                     scale = targetWidth / width;
27972                 }
27973                 
27974                 if(x > 0 && y == 0){
27975                     scale = targetHeight / height;
27976                 }
27977                 
27978                 if(x > 0 && y > 0){
27979                     scale = targetWidth / width;
27980                     
27981                     if(width < height){
27982                         scale = targetHeight / height;
27983                     }
27984                 }
27985                 
27986                 context.scale(scale, scale);
27987                 
27988                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27989                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27990
27991                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27992                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27993
27994                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27995                 
27996                 break;
27997             case 90 : 
27998                 
27999                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28000                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28001                 
28002                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28003                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28004                 
28005                 var targetWidth = this.minWidth - 2 * x;
28006                 var targetHeight = this.minHeight - 2 * y;
28007                 
28008                 var scale = 1;
28009                 
28010                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28011                     scale = targetWidth / width;
28012                 }
28013                 
28014                 if(x > 0 && y == 0){
28015                     scale = targetHeight / height;
28016                 }
28017                 
28018                 if(x > 0 && y > 0){
28019                     scale = targetWidth / width;
28020                     
28021                     if(width < height){
28022                         scale = targetHeight / height;
28023                     }
28024                 }
28025                 
28026                 context.scale(scale, scale);
28027                 
28028                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28029                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28030
28031                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28032                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28033                 
28034                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28035                 
28036                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28037                 
28038                 break;
28039             case 180 :
28040                 
28041                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28042                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28043                 
28044                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28045                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28046                 
28047                 var targetWidth = this.minWidth - 2 * x;
28048                 var targetHeight = this.minHeight - 2 * y;
28049                 
28050                 var scale = 1;
28051                 
28052                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28053                     scale = targetWidth / width;
28054                 }
28055                 
28056                 if(x > 0 && y == 0){
28057                     scale = targetHeight / height;
28058                 }
28059                 
28060                 if(x > 0 && y > 0){
28061                     scale = targetWidth / width;
28062                     
28063                     if(width < height){
28064                         scale = targetHeight / height;
28065                     }
28066                 }
28067                 
28068                 context.scale(scale, scale);
28069                 
28070                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28071                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28072
28073                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28074                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28075
28076                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28077                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28078                 
28079                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28080                 
28081                 break;
28082             case 270 :
28083                 
28084                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28085                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28086                 
28087                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28088                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28089                 
28090                 var targetWidth = this.minWidth - 2 * x;
28091                 var targetHeight = this.minHeight - 2 * y;
28092                 
28093                 var scale = 1;
28094                 
28095                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28096                     scale = targetWidth / width;
28097                 }
28098                 
28099                 if(x > 0 && y == 0){
28100                     scale = targetHeight / height;
28101                 }
28102                 
28103                 if(x > 0 && y > 0){
28104                     scale = targetWidth / width;
28105                     
28106                     if(width < height){
28107                         scale = targetHeight / height;
28108                     }
28109                 }
28110                 
28111                 context.scale(scale, scale);
28112                 
28113                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28114                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28115
28116                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28117                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28118                 
28119                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28120                 
28121                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28122                 
28123                 break;
28124             default : 
28125                 break;
28126         }
28127         
28128         this.cropData = canvas.toDataURL(this.cropType);
28129         
28130         if(this.fireEvent('crop', this, this.cropData) !== false){
28131             this.process(this.file, this.cropData);
28132         }
28133         
28134         return;
28135         
28136     },
28137     
28138     setThumbBoxSize : function()
28139     {
28140         var width, height;
28141         
28142         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28143             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28144             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28145             
28146             this.minWidth = width;
28147             this.minHeight = height;
28148             
28149             if(this.rotate == 90 || this.rotate == 270){
28150                 this.minWidth = height;
28151                 this.minHeight = width;
28152             }
28153         }
28154         
28155         height = 300;
28156         width = Math.ceil(this.minWidth * height / this.minHeight);
28157         
28158         if(this.minWidth > this.minHeight){
28159             width = 300;
28160             height = Math.ceil(this.minHeight * width / this.minWidth);
28161         }
28162         
28163         this.thumbEl.setStyle({
28164             width : width + 'px',
28165             height : height + 'px'
28166         });
28167
28168         return;
28169             
28170     },
28171     
28172     setThumbBoxPosition : function()
28173     {
28174         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28175         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28176         
28177         this.thumbEl.setLeft(x);
28178         this.thumbEl.setTop(y);
28179         
28180     },
28181     
28182     baseRotateLevel : function()
28183     {
28184         this.baseRotate = 1;
28185         
28186         if(
28187                 typeof(this.exif) != 'undefined' &&
28188                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28189                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28190         ){
28191             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28192         }
28193         
28194         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28195         
28196     },
28197     
28198     baseScaleLevel : function()
28199     {
28200         var width, height;
28201         
28202         if(this.isDocument){
28203             
28204             if(this.baseRotate == 6 || this.baseRotate == 8){
28205             
28206                 height = this.thumbEl.getHeight();
28207                 this.baseScale = height / this.imageEl.OriginWidth;
28208
28209                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28210                     width = this.thumbEl.getWidth();
28211                     this.baseScale = width / this.imageEl.OriginHeight;
28212                 }
28213
28214                 return;
28215             }
28216
28217             height = this.thumbEl.getHeight();
28218             this.baseScale = height / this.imageEl.OriginHeight;
28219
28220             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28221                 width = this.thumbEl.getWidth();
28222                 this.baseScale = width / this.imageEl.OriginWidth;
28223             }
28224
28225             return;
28226         }
28227         
28228         if(this.baseRotate == 6 || this.baseRotate == 8){
28229             
28230             width = this.thumbEl.getHeight();
28231             this.baseScale = width / this.imageEl.OriginHeight;
28232             
28233             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28234                 height = this.thumbEl.getWidth();
28235                 this.baseScale = height / this.imageEl.OriginHeight;
28236             }
28237             
28238             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28239                 height = this.thumbEl.getWidth();
28240                 this.baseScale = height / this.imageEl.OriginHeight;
28241                 
28242                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28243                     width = this.thumbEl.getHeight();
28244                     this.baseScale = width / this.imageEl.OriginWidth;
28245                 }
28246             }
28247             
28248             return;
28249         }
28250         
28251         width = this.thumbEl.getWidth();
28252         this.baseScale = width / this.imageEl.OriginWidth;
28253         
28254         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28255             height = this.thumbEl.getHeight();
28256             this.baseScale = height / this.imageEl.OriginHeight;
28257         }
28258         
28259         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28260             
28261             height = this.thumbEl.getHeight();
28262             this.baseScale = height / this.imageEl.OriginHeight;
28263             
28264             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28265                 width = this.thumbEl.getWidth();
28266                 this.baseScale = width / this.imageEl.OriginWidth;
28267             }
28268             
28269         }
28270         
28271         return;
28272     },
28273     
28274     getScaleLevel : function()
28275     {
28276         return this.baseScale * Math.pow(1.1, this.scale);
28277     },
28278     
28279     onTouchStart : function(e)
28280     {
28281         if(!this.canvasLoaded){
28282             this.beforeSelectFile(e);
28283             return;
28284         }
28285         
28286         var touches = e.browserEvent.touches;
28287         
28288         if(!touches){
28289             return;
28290         }
28291         
28292         if(touches.length == 1){
28293             this.onMouseDown(e);
28294             return;
28295         }
28296         
28297         if(touches.length != 2){
28298             return;
28299         }
28300         
28301         var coords = [];
28302         
28303         for(var i = 0, finger; finger = touches[i]; i++){
28304             coords.push(finger.pageX, finger.pageY);
28305         }
28306         
28307         var x = Math.pow(coords[0] - coords[2], 2);
28308         var y = Math.pow(coords[1] - coords[3], 2);
28309         
28310         this.startDistance = Math.sqrt(x + y);
28311         
28312         this.startScale = this.scale;
28313         
28314         this.pinching = true;
28315         this.dragable = false;
28316         
28317     },
28318     
28319     onTouchMove : function(e)
28320     {
28321         if(!this.pinching && !this.dragable){
28322             return;
28323         }
28324         
28325         var touches = e.browserEvent.touches;
28326         
28327         if(!touches){
28328             return;
28329         }
28330         
28331         if(this.dragable){
28332             this.onMouseMove(e);
28333             return;
28334         }
28335         
28336         var coords = [];
28337         
28338         for(var i = 0, finger; finger = touches[i]; i++){
28339             coords.push(finger.pageX, finger.pageY);
28340         }
28341         
28342         var x = Math.pow(coords[0] - coords[2], 2);
28343         var y = Math.pow(coords[1] - coords[3], 2);
28344         
28345         this.endDistance = Math.sqrt(x + y);
28346         
28347         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28348         
28349         if(!this.zoomable()){
28350             this.scale = this.startScale;
28351             return;
28352         }
28353         
28354         this.draw();
28355         
28356     },
28357     
28358     onTouchEnd : function(e)
28359     {
28360         this.pinching = false;
28361         this.dragable = false;
28362         
28363     },
28364     
28365     process : function(file, crop)
28366     {
28367         if(this.loadMask){
28368             this.maskEl.mask(this.loadingText);
28369         }
28370         
28371         this.xhr = new XMLHttpRequest();
28372         
28373         file.xhr = this.xhr;
28374
28375         this.xhr.open(this.method, this.url, true);
28376         
28377         var headers = {
28378             "Accept": "application/json",
28379             "Cache-Control": "no-cache",
28380             "X-Requested-With": "XMLHttpRequest"
28381         };
28382         
28383         for (var headerName in headers) {
28384             var headerValue = headers[headerName];
28385             if (headerValue) {
28386                 this.xhr.setRequestHeader(headerName, headerValue);
28387             }
28388         }
28389         
28390         var _this = this;
28391         
28392         this.xhr.onload = function()
28393         {
28394             _this.xhrOnLoad(_this.xhr);
28395         }
28396         
28397         this.xhr.onerror = function()
28398         {
28399             _this.xhrOnError(_this.xhr);
28400         }
28401         
28402         var formData = new FormData();
28403
28404         formData.append('returnHTML', 'NO');
28405         
28406         if(crop){
28407             formData.append('crop', crop);
28408         }
28409         
28410         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28411             formData.append(this.paramName, file, file.name);
28412         }
28413         
28414         if(typeof(file.filename) != 'undefined'){
28415             formData.append('filename', file.filename);
28416         }
28417         
28418         if(typeof(file.mimetype) != 'undefined'){
28419             formData.append('mimetype', file.mimetype);
28420         }
28421         
28422         if(this.fireEvent('arrange', this, formData) != false){
28423             this.xhr.send(formData);
28424         };
28425     },
28426     
28427     xhrOnLoad : function(xhr)
28428     {
28429         if(this.loadMask){
28430             this.maskEl.unmask();
28431         }
28432         
28433         if (xhr.readyState !== 4) {
28434             this.fireEvent('exception', this, xhr);
28435             return;
28436         }
28437
28438         var response = Roo.decode(xhr.responseText);
28439         
28440         if(!response.success){
28441             this.fireEvent('exception', this, xhr);
28442             return;
28443         }
28444         
28445         var response = Roo.decode(xhr.responseText);
28446         
28447         this.fireEvent('upload', this, response);
28448         
28449     },
28450     
28451     xhrOnError : function()
28452     {
28453         if(this.loadMask){
28454             this.maskEl.unmask();
28455         }
28456         
28457         Roo.log('xhr on error');
28458         
28459         var response = Roo.decode(xhr.responseText);
28460           
28461         Roo.log(response);
28462         
28463     },
28464     
28465     prepare : function(file)
28466     {   
28467         if(this.loadMask){
28468             this.maskEl.mask(this.loadingText);
28469         }
28470         
28471         this.file = false;
28472         this.exif = {};
28473         
28474         if(typeof(file) === 'string'){
28475             this.loadCanvas(file);
28476             return;
28477         }
28478         
28479         if(!file || !this.urlAPI){
28480             return;
28481         }
28482         
28483         this.file = file;
28484         this.cropType = file.type;
28485         
28486         var _this = this;
28487         
28488         if(this.fireEvent('prepare', this, this.file) != false){
28489             
28490             var reader = new FileReader();
28491             
28492             reader.onload = function (e) {
28493                 if (e.target.error) {
28494                     Roo.log(e.target.error);
28495                     return;
28496                 }
28497                 
28498                 var buffer = e.target.result,
28499                     dataView = new DataView(buffer),
28500                     offset = 2,
28501                     maxOffset = dataView.byteLength - 4,
28502                     markerBytes,
28503                     markerLength;
28504                 
28505                 if (dataView.getUint16(0) === 0xffd8) {
28506                     while (offset < maxOffset) {
28507                         markerBytes = dataView.getUint16(offset);
28508                         
28509                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28510                             markerLength = dataView.getUint16(offset + 2) + 2;
28511                             if (offset + markerLength > dataView.byteLength) {
28512                                 Roo.log('Invalid meta data: Invalid segment size.');
28513                                 break;
28514                             }
28515                             
28516                             if(markerBytes == 0xffe1){
28517                                 _this.parseExifData(
28518                                     dataView,
28519                                     offset,
28520                                     markerLength
28521                                 );
28522                             }
28523                             
28524                             offset += markerLength;
28525                             
28526                             continue;
28527                         }
28528                         
28529                         break;
28530                     }
28531                     
28532                 }
28533                 
28534                 var url = _this.urlAPI.createObjectURL(_this.file);
28535                 
28536                 _this.loadCanvas(url);
28537                 
28538                 return;
28539             }
28540             
28541             reader.readAsArrayBuffer(this.file);
28542             
28543         }
28544         
28545     },
28546     
28547     parseExifData : function(dataView, offset, length)
28548     {
28549         var tiffOffset = offset + 10,
28550             littleEndian,
28551             dirOffset;
28552     
28553         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28554             // No Exif data, might be XMP data instead
28555             return;
28556         }
28557         
28558         // Check for the ASCII code for "Exif" (0x45786966):
28559         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28560             // No Exif data, might be XMP data instead
28561             return;
28562         }
28563         if (tiffOffset + 8 > dataView.byteLength) {
28564             Roo.log('Invalid Exif data: Invalid segment size.');
28565             return;
28566         }
28567         // Check for the two null bytes:
28568         if (dataView.getUint16(offset + 8) !== 0x0000) {
28569             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28570             return;
28571         }
28572         // Check the byte alignment:
28573         switch (dataView.getUint16(tiffOffset)) {
28574         case 0x4949:
28575             littleEndian = true;
28576             break;
28577         case 0x4D4D:
28578             littleEndian = false;
28579             break;
28580         default:
28581             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28582             return;
28583         }
28584         // Check for the TIFF tag marker (0x002A):
28585         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28586             Roo.log('Invalid Exif data: Missing TIFF marker.');
28587             return;
28588         }
28589         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28590         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28591         
28592         this.parseExifTags(
28593             dataView,
28594             tiffOffset,
28595             tiffOffset + dirOffset,
28596             littleEndian
28597         );
28598     },
28599     
28600     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28601     {
28602         var tagsNumber,
28603             dirEndOffset,
28604             i;
28605         if (dirOffset + 6 > dataView.byteLength) {
28606             Roo.log('Invalid Exif data: Invalid directory offset.');
28607             return;
28608         }
28609         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28610         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28611         if (dirEndOffset + 4 > dataView.byteLength) {
28612             Roo.log('Invalid Exif data: Invalid directory size.');
28613             return;
28614         }
28615         for (i = 0; i < tagsNumber; i += 1) {
28616             this.parseExifTag(
28617                 dataView,
28618                 tiffOffset,
28619                 dirOffset + 2 + 12 * i, // tag offset
28620                 littleEndian
28621             );
28622         }
28623         // Return the offset to the next directory:
28624         return dataView.getUint32(dirEndOffset, littleEndian);
28625     },
28626     
28627     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28628     {
28629         var tag = dataView.getUint16(offset, littleEndian);
28630         
28631         this.exif[tag] = this.getExifValue(
28632             dataView,
28633             tiffOffset,
28634             offset,
28635             dataView.getUint16(offset + 2, littleEndian), // tag type
28636             dataView.getUint32(offset + 4, littleEndian), // tag length
28637             littleEndian
28638         );
28639     },
28640     
28641     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28642     {
28643         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28644             tagSize,
28645             dataOffset,
28646             values,
28647             i,
28648             str,
28649             c;
28650     
28651         if (!tagType) {
28652             Roo.log('Invalid Exif data: Invalid tag type.');
28653             return;
28654         }
28655         
28656         tagSize = tagType.size * length;
28657         // Determine if the value is contained in the dataOffset bytes,
28658         // or if the value at the dataOffset is a pointer to the actual data:
28659         dataOffset = tagSize > 4 ?
28660                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28661         if (dataOffset + tagSize > dataView.byteLength) {
28662             Roo.log('Invalid Exif data: Invalid data offset.');
28663             return;
28664         }
28665         if (length === 1) {
28666             return tagType.getValue(dataView, dataOffset, littleEndian);
28667         }
28668         values = [];
28669         for (i = 0; i < length; i += 1) {
28670             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28671         }
28672         
28673         if (tagType.ascii) {
28674             str = '';
28675             // Concatenate the chars:
28676             for (i = 0; i < values.length; i += 1) {
28677                 c = values[i];
28678                 // Ignore the terminating NULL byte(s):
28679                 if (c === '\u0000') {
28680                     break;
28681                 }
28682                 str += c;
28683             }
28684             return str;
28685         }
28686         return values;
28687     }
28688     
28689 });
28690
28691 Roo.apply(Roo.bootstrap.UploadCropbox, {
28692     tags : {
28693         'Orientation': 0x0112
28694     },
28695     
28696     Orientation: {
28697             1: 0, //'top-left',
28698 //            2: 'top-right',
28699             3: 180, //'bottom-right',
28700 //            4: 'bottom-left',
28701 //            5: 'left-top',
28702             6: 90, //'right-top',
28703 //            7: 'right-bottom',
28704             8: 270 //'left-bottom'
28705     },
28706     
28707     exifTagTypes : {
28708         // byte, 8-bit unsigned int:
28709         1: {
28710             getValue: function (dataView, dataOffset) {
28711                 return dataView.getUint8(dataOffset);
28712             },
28713             size: 1
28714         },
28715         // ascii, 8-bit byte:
28716         2: {
28717             getValue: function (dataView, dataOffset) {
28718                 return String.fromCharCode(dataView.getUint8(dataOffset));
28719             },
28720             size: 1,
28721             ascii: true
28722         },
28723         // short, 16 bit int:
28724         3: {
28725             getValue: function (dataView, dataOffset, littleEndian) {
28726                 return dataView.getUint16(dataOffset, littleEndian);
28727             },
28728             size: 2
28729         },
28730         // long, 32 bit int:
28731         4: {
28732             getValue: function (dataView, dataOffset, littleEndian) {
28733                 return dataView.getUint32(dataOffset, littleEndian);
28734             },
28735             size: 4
28736         },
28737         // rational = two long values, first is numerator, second is denominator:
28738         5: {
28739             getValue: function (dataView, dataOffset, littleEndian) {
28740                 return dataView.getUint32(dataOffset, littleEndian) /
28741                     dataView.getUint32(dataOffset + 4, littleEndian);
28742             },
28743             size: 8
28744         },
28745         // slong, 32 bit signed int:
28746         9: {
28747             getValue: function (dataView, dataOffset, littleEndian) {
28748                 return dataView.getInt32(dataOffset, littleEndian);
28749             },
28750             size: 4
28751         },
28752         // srational, two slongs, first is numerator, second is denominator:
28753         10: {
28754             getValue: function (dataView, dataOffset, littleEndian) {
28755                 return dataView.getInt32(dataOffset, littleEndian) /
28756                     dataView.getInt32(dataOffset + 4, littleEndian);
28757             },
28758             size: 8
28759         }
28760     },
28761     
28762     footer : {
28763         STANDARD : [
28764             {
28765                 tag : 'div',
28766                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28767                 action : 'rotate-left',
28768                 cn : [
28769                     {
28770                         tag : 'button',
28771                         cls : 'btn btn-default',
28772                         html : '<i class="fa fa-undo"></i>'
28773                     }
28774                 ]
28775             },
28776             {
28777                 tag : 'div',
28778                 cls : 'btn-group roo-upload-cropbox-picture',
28779                 action : 'picture',
28780                 cn : [
28781                     {
28782                         tag : 'button',
28783                         cls : 'btn btn-default',
28784                         html : '<i class="fa fa-picture-o"></i>'
28785                     }
28786                 ]
28787             },
28788             {
28789                 tag : 'div',
28790                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28791                 action : 'rotate-right',
28792                 cn : [
28793                     {
28794                         tag : 'button',
28795                         cls : 'btn btn-default',
28796                         html : '<i class="fa fa-repeat"></i>'
28797                     }
28798                 ]
28799             }
28800         ],
28801         DOCUMENT : [
28802             {
28803                 tag : 'div',
28804                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28805                 action : 'rotate-left',
28806                 cn : [
28807                     {
28808                         tag : 'button',
28809                         cls : 'btn btn-default',
28810                         html : '<i class="fa fa-undo"></i>'
28811                     }
28812                 ]
28813             },
28814             {
28815                 tag : 'div',
28816                 cls : 'btn-group roo-upload-cropbox-download',
28817                 action : 'download',
28818                 cn : [
28819                     {
28820                         tag : 'button',
28821                         cls : 'btn btn-default',
28822                         html : '<i class="fa fa-download"></i>'
28823                     }
28824                 ]
28825             },
28826             {
28827                 tag : 'div',
28828                 cls : 'btn-group roo-upload-cropbox-crop',
28829                 action : 'crop',
28830                 cn : [
28831                     {
28832                         tag : 'button',
28833                         cls : 'btn btn-default',
28834                         html : '<i class="fa fa-crop"></i>'
28835                     }
28836                 ]
28837             },
28838             {
28839                 tag : 'div',
28840                 cls : 'btn-group roo-upload-cropbox-trash',
28841                 action : 'trash',
28842                 cn : [
28843                     {
28844                         tag : 'button',
28845                         cls : 'btn btn-default',
28846                         html : '<i class="fa fa-trash"></i>'
28847                     }
28848                 ]
28849             },
28850             {
28851                 tag : 'div',
28852                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28853                 action : 'rotate-right',
28854                 cn : [
28855                     {
28856                         tag : 'button',
28857                         cls : 'btn btn-default',
28858                         html : '<i class="fa fa-repeat"></i>'
28859                     }
28860                 ]
28861             }
28862         ],
28863         ROTATOR : [
28864             {
28865                 tag : 'div',
28866                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28867                 action : 'rotate-left',
28868                 cn : [
28869                     {
28870                         tag : 'button',
28871                         cls : 'btn btn-default',
28872                         html : '<i class="fa fa-undo"></i>'
28873                     }
28874                 ]
28875             },
28876             {
28877                 tag : 'div',
28878                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28879                 action : 'rotate-right',
28880                 cn : [
28881                     {
28882                         tag : 'button',
28883                         cls : 'btn btn-default',
28884                         html : '<i class="fa fa-repeat"></i>'
28885                     }
28886                 ]
28887             }
28888         ]
28889     }
28890 });
28891
28892 /*
28893 * Licence: LGPL
28894 */
28895
28896 /**
28897  * @class Roo.bootstrap.DocumentManager
28898  * @extends Roo.bootstrap.Component
28899  * Bootstrap DocumentManager class
28900  * @cfg {String} paramName default 'imageUpload'
28901  * @cfg {String} toolTipName default 'filename'
28902  * @cfg {String} method default POST
28903  * @cfg {String} url action url
28904  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28905  * @cfg {Boolean} multiple multiple upload default true
28906  * @cfg {Number} thumbSize default 300
28907  * @cfg {String} fieldLabel
28908  * @cfg {Number} labelWidth default 4
28909  * @cfg {String} labelAlign (left|top) default left
28910  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28911 * @cfg {Number} labellg set the width of label (1-12)
28912  * @cfg {Number} labelmd set the width of label (1-12)
28913  * @cfg {Number} labelsm set the width of label (1-12)
28914  * @cfg {Number} labelxs set the width of label (1-12)
28915  * 
28916  * @constructor
28917  * Create a new DocumentManager
28918  * @param {Object} config The config object
28919  */
28920
28921 Roo.bootstrap.DocumentManager = function(config){
28922     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28923     
28924     this.files = [];
28925     this.delegates = [];
28926     
28927     this.addEvents({
28928         /**
28929          * @event initial
28930          * Fire when initial the DocumentManager
28931          * @param {Roo.bootstrap.DocumentManager} this
28932          */
28933         "initial" : true,
28934         /**
28935          * @event inspect
28936          * inspect selected file
28937          * @param {Roo.bootstrap.DocumentManager} this
28938          * @param {File} file
28939          */
28940         "inspect" : true,
28941         /**
28942          * @event exception
28943          * Fire when xhr load exception
28944          * @param {Roo.bootstrap.DocumentManager} this
28945          * @param {XMLHttpRequest} xhr
28946          */
28947         "exception" : true,
28948         /**
28949          * @event afterupload
28950          * Fire when xhr load exception
28951          * @param {Roo.bootstrap.DocumentManager} this
28952          * @param {XMLHttpRequest} xhr
28953          */
28954         "afterupload" : true,
28955         /**
28956          * @event prepare
28957          * prepare the form data
28958          * @param {Roo.bootstrap.DocumentManager} this
28959          * @param {Object} formData
28960          */
28961         "prepare" : true,
28962         /**
28963          * @event remove
28964          * Fire when remove the file
28965          * @param {Roo.bootstrap.DocumentManager} this
28966          * @param {Object} file
28967          */
28968         "remove" : true,
28969         /**
28970          * @event refresh
28971          * Fire after refresh the file
28972          * @param {Roo.bootstrap.DocumentManager} this
28973          */
28974         "refresh" : true,
28975         /**
28976          * @event click
28977          * Fire after click the image
28978          * @param {Roo.bootstrap.DocumentManager} this
28979          * @param {Object} file
28980          */
28981         "click" : true,
28982         /**
28983          * @event edit
28984          * Fire when upload a image and editable set to true
28985          * @param {Roo.bootstrap.DocumentManager} this
28986          * @param {Object} file
28987          */
28988         "edit" : true,
28989         /**
28990          * @event beforeselectfile
28991          * Fire before select file
28992          * @param {Roo.bootstrap.DocumentManager} this
28993          */
28994         "beforeselectfile" : true,
28995         /**
28996          * @event process
28997          * Fire before process file
28998          * @param {Roo.bootstrap.DocumentManager} this
28999          * @param {Object} file
29000          */
29001         "process" : true,
29002         /**
29003          * @event previewrendered
29004          * Fire when preview rendered
29005          * @param {Roo.bootstrap.DocumentManager} this
29006          * @param {Object} file
29007          */
29008         "previewrendered" : true,
29009         /**
29010          */
29011         "previewResize" : true
29012         
29013     });
29014 };
29015
29016 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29017     
29018     boxes : 0,
29019     inputName : '',
29020     thumbSize : 300,
29021     multiple : true,
29022     files : false,
29023     method : 'POST',
29024     url : '',
29025     paramName : 'imageUpload',
29026     toolTipName : 'filename',
29027     fieldLabel : '',
29028     labelWidth : 4,
29029     labelAlign : 'left',
29030     editable : true,
29031     delegates : false,
29032     xhr : false, 
29033     
29034     labellg : 0,
29035     labelmd : 0,
29036     labelsm : 0,
29037     labelxs : 0,
29038     
29039     getAutoCreate : function()
29040     {   
29041         var managerWidget = {
29042             tag : 'div',
29043             cls : 'roo-document-manager',
29044             cn : [
29045                 {
29046                     tag : 'input',
29047                     cls : 'roo-document-manager-selector',
29048                     type : 'file'
29049                 },
29050                 {
29051                     tag : 'div',
29052                     cls : 'roo-document-manager-uploader',
29053                     cn : [
29054                         {
29055                             tag : 'div',
29056                             cls : 'roo-document-manager-upload-btn',
29057                             html : '<i class="fa fa-plus"></i>'
29058                         }
29059                     ]
29060                     
29061                 }
29062             ]
29063         };
29064         
29065         var content = [
29066             {
29067                 tag : 'div',
29068                 cls : 'column col-md-12',
29069                 cn : managerWidget
29070             }
29071         ];
29072         
29073         if(this.fieldLabel.length){
29074             
29075             content = [
29076                 {
29077                     tag : 'div',
29078                     cls : 'column col-md-12',
29079                     html : this.fieldLabel
29080                 },
29081                 {
29082                     tag : 'div',
29083                     cls : 'column col-md-12',
29084                     cn : managerWidget
29085                 }
29086             ];
29087
29088             if(this.labelAlign == 'left'){
29089                 content = [
29090                     {
29091                         tag : 'div',
29092                         cls : 'column',
29093                         html : this.fieldLabel
29094                     },
29095                     {
29096                         tag : 'div',
29097                         cls : 'column',
29098                         cn : managerWidget
29099                     }
29100                 ];
29101                 
29102                 if(this.labelWidth > 12){
29103                     content[0].style = "width: " + this.labelWidth + 'px';
29104                 }
29105
29106                 if(this.labelWidth < 13 && this.labelmd == 0){
29107                     this.labelmd = this.labelWidth;
29108                 }
29109
29110                 if(this.labellg > 0){
29111                     content[0].cls += ' col-lg-' + this.labellg;
29112                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29113                 }
29114
29115                 if(this.labelmd > 0){
29116                     content[0].cls += ' col-md-' + this.labelmd;
29117                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29118                 }
29119
29120                 if(this.labelsm > 0){
29121                     content[0].cls += ' col-sm-' + this.labelsm;
29122                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29123                 }
29124
29125                 if(this.labelxs > 0){
29126                     content[0].cls += ' col-xs-' + this.labelxs;
29127                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29128                 }
29129                 
29130             }
29131         }
29132         
29133         var cfg = {
29134             tag : 'div',
29135             cls : 'row clearfix',
29136             cn : content
29137         };
29138         
29139         return cfg;
29140         
29141     },
29142     
29143     initEvents : function()
29144     {
29145         this.managerEl = this.el.select('.roo-document-manager', true).first();
29146         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29147         
29148         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29149         this.selectorEl.hide();
29150         
29151         if(this.multiple){
29152             this.selectorEl.attr('multiple', 'multiple');
29153         }
29154         
29155         this.selectorEl.on('change', this.onFileSelected, this);
29156         
29157         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29158         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29159         
29160         this.uploader.on('click', this.onUploaderClick, this);
29161         
29162         this.renderProgressDialog();
29163         
29164         var _this = this;
29165         
29166         window.addEventListener("resize", function() { _this.refresh(); } );
29167         
29168         this.fireEvent('initial', this);
29169     },
29170     
29171     renderProgressDialog : function()
29172     {
29173         var _this = this;
29174         
29175         this.progressDialog = new Roo.bootstrap.Modal({
29176             cls : 'roo-document-manager-progress-dialog',
29177             allow_close : false,
29178             title : '',
29179             buttons : [
29180                 {
29181                     name  :'cancel',
29182                     weight : 'danger',
29183                     html : 'Cancel'
29184                 }
29185             ], 
29186             listeners : { 
29187                 btnclick : function() {
29188                     _this.uploadCancel();
29189                     this.hide();
29190                 }
29191             }
29192         });
29193          
29194         this.progressDialog.render(Roo.get(document.body));
29195          
29196         this.progress = new Roo.bootstrap.Progress({
29197             cls : 'roo-document-manager-progress',
29198             active : true,
29199             striped : true
29200         });
29201         
29202         this.progress.render(this.progressDialog.getChildContainer());
29203         
29204         this.progressBar = new Roo.bootstrap.ProgressBar({
29205             cls : 'roo-document-manager-progress-bar',
29206             aria_valuenow : 0,
29207             aria_valuemin : 0,
29208             aria_valuemax : 12,
29209             panel : 'success'
29210         });
29211         
29212         this.progressBar.render(this.progress.getChildContainer());
29213     },
29214     
29215     onUploaderClick : function(e)
29216     {
29217         e.preventDefault();
29218      
29219         if(this.fireEvent('beforeselectfile', this) != false){
29220             this.selectorEl.dom.click();
29221         }
29222         
29223     },
29224     
29225     onFileSelected : function(e)
29226     {
29227         e.preventDefault();
29228         
29229         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29230             return;
29231         }
29232         
29233         Roo.each(this.selectorEl.dom.files, function(file){
29234             if(this.fireEvent('inspect', this, file) != false){
29235                 this.files.push(file);
29236             }
29237         }, this);
29238         
29239         this.queue();
29240         
29241     },
29242     
29243     queue : function()
29244     {
29245         this.selectorEl.dom.value = '';
29246         
29247         if(!this.files || !this.files.length){
29248             return;
29249         }
29250         
29251         if(this.boxes > 0 && this.files.length > this.boxes){
29252             this.files = this.files.slice(0, this.boxes);
29253         }
29254         
29255         this.uploader.show();
29256         
29257         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29258             this.uploader.hide();
29259         }
29260         
29261         var _this = this;
29262         
29263         var files = [];
29264         
29265         var docs = [];
29266         
29267         Roo.each(this.files, function(file){
29268             
29269             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29270                 var f = this.renderPreview(file);
29271                 files.push(f);
29272                 return;
29273             }
29274             
29275             if(file.type.indexOf('image') != -1){
29276                 this.delegates.push(
29277                     (function(){
29278                         _this.process(file);
29279                     }).createDelegate(this)
29280                 );
29281         
29282                 return;
29283             }
29284             
29285             docs.push(
29286                 (function(){
29287                     _this.process(file);
29288                 }).createDelegate(this)
29289             );
29290             
29291         }, this);
29292         
29293         this.files = files;
29294         
29295         this.delegates = this.delegates.concat(docs);
29296         
29297         if(!this.delegates.length){
29298             this.refresh();
29299             return;
29300         }
29301         
29302         this.progressBar.aria_valuemax = this.delegates.length;
29303         
29304         this.arrange();
29305         
29306         return;
29307     },
29308     
29309     arrange : function()
29310     {
29311         if(!this.delegates.length){
29312             this.progressDialog.hide();
29313             this.refresh();
29314             return;
29315         }
29316         
29317         var delegate = this.delegates.shift();
29318         
29319         this.progressDialog.show();
29320         
29321         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29322         
29323         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29324         
29325         delegate();
29326     },
29327     
29328     refresh : function()
29329     {
29330         this.uploader.show();
29331         
29332         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29333             this.uploader.hide();
29334         }
29335         
29336         Roo.isTouch ? this.closable(false) : this.closable(true);
29337         
29338         this.fireEvent('refresh', this);
29339     },
29340     
29341     onRemove : function(e, el, o)
29342     {
29343         e.preventDefault();
29344         
29345         this.fireEvent('remove', this, o);
29346         
29347     },
29348     
29349     remove : function(o)
29350     {
29351         var files = [];
29352         
29353         Roo.each(this.files, function(file){
29354             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29355                 files.push(file);
29356                 return;
29357             }
29358
29359             o.target.remove();
29360
29361         }, this);
29362         
29363         this.files = files;
29364         
29365         this.refresh();
29366     },
29367     
29368     clear : function()
29369     {
29370         Roo.each(this.files, function(file){
29371             if(!file.target){
29372                 return;
29373             }
29374             
29375             file.target.remove();
29376
29377         }, this);
29378         
29379         this.files = [];
29380         
29381         this.refresh();
29382     },
29383     
29384     onClick : function(e, el, o)
29385     {
29386         e.preventDefault();
29387         
29388         this.fireEvent('click', this, o);
29389         
29390     },
29391     
29392     closable : function(closable)
29393     {
29394         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29395             
29396             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29397             
29398             if(closable){
29399                 el.show();
29400                 return;
29401             }
29402             
29403             el.hide();
29404             
29405         }, this);
29406     },
29407     
29408     xhrOnLoad : function(xhr)
29409     {
29410         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29411             el.remove();
29412         }, this);
29413         
29414         if (xhr.readyState !== 4) {
29415             this.arrange();
29416             this.fireEvent('exception', this, xhr);
29417             return;
29418         }
29419
29420         var response = Roo.decode(xhr.responseText);
29421         
29422         if(!response.success){
29423             this.arrange();
29424             this.fireEvent('exception', this, xhr);
29425             return;
29426         }
29427         
29428         var file = this.renderPreview(response.data);
29429         
29430         this.files.push(file);
29431         
29432         this.arrange();
29433         
29434         this.fireEvent('afterupload', this, xhr);
29435         
29436     },
29437     
29438     xhrOnError : function(xhr)
29439     {
29440         Roo.log('xhr on error');
29441         
29442         var response = Roo.decode(xhr.responseText);
29443           
29444         Roo.log(response);
29445         
29446         this.arrange();
29447     },
29448     
29449     process : function(file)
29450     {
29451         if(this.fireEvent('process', this, file) !== false){
29452             if(this.editable && file.type.indexOf('image') != -1){
29453                 this.fireEvent('edit', this, file);
29454                 return;
29455             }
29456
29457             this.uploadStart(file, false);
29458
29459             return;
29460         }
29461         
29462     },
29463     
29464     uploadStart : function(file, crop)
29465     {
29466         this.xhr = new XMLHttpRequest();
29467         
29468         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29469             this.arrange();
29470             return;
29471         }
29472         
29473         file.xhr = this.xhr;
29474             
29475         this.managerEl.createChild({
29476             tag : 'div',
29477             cls : 'roo-document-manager-loading',
29478             cn : [
29479                 {
29480                     tag : 'div',
29481                     tooltip : file.name,
29482                     cls : 'roo-document-manager-thumb',
29483                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29484                 }
29485             ]
29486
29487         });
29488
29489         this.xhr.open(this.method, this.url, true);
29490         
29491         var headers = {
29492             "Accept": "application/json",
29493             "Cache-Control": "no-cache",
29494             "X-Requested-With": "XMLHttpRequest"
29495         };
29496         
29497         for (var headerName in headers) {
29498             var headerValue = headers[headerName];
29499             if (headerValue) {
29500                 this.xhr.setRequestHeader(headerName, headerValue);
29501             }
29502         }
29503         
29504         var _this = this;
29505         
29506         this.xhr.onload = function()
29507         {
29508             _this.xhrOnLoad(_this.xhr);
29509         }
29510         
29511         this.xhr.onerror = function()
29512         {
29513             _this.xhrOnError(_this.xhr);
29514         }
29515         
29516         var formData = new FormData();
29517
29518         formData.append('returnHTML', 'NO');
29519         
29520         if(crop){
29521             formData.append('crop', crop);
29522         }
29523         
29524         formData.append(this.paramName, file, file.name);
29525         
29526         var options = {
29527             file : file, 
29528             manually : false
29529         };
29530         
29531         if(this.fireEvent('prepare', this, formData, options) != false){
29532             
29533             if(options.manually){
29534                 return;
29535             }
29536             
29537             this.xhr.send(formData);
29538             return;
29539         };
29540         
29541         this.uploadCancel();
29542     },
29543     
29544     uploadCancel : function()
29545     {
29546         if (this.xhr) {
29547             this.xhr.abort();
29548         }
29549         
29550         this.delegates = [];
29551         
29552         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29553             el.remove();
29554         }, this);
29555         
29556         this.arrange();
29557     },
29558     
29559     renderPreview : function(file)
29560     {
29561         if(typeof(file.target) != 'undefined' && file.target){
29562             return file;
29563         }
29564         
29565         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29566         
29567         var previewEl = this.managerEl.createChild({
29568             tag : 'div',
29569             cls : 'roo-document-manager-preview',
29570             cn : [
29571                 {
29572                     tag : 'div',
29573                     tooltip : file[this.toolTipName],
29574                     cls : 'roo-document-manager-thumb',
29575                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29576                 },
29577                 {
29578                     tag : 'button',
29579                     cls : 'close',
29580                     html : '<i class="fa fa-times-circle"></i>'
29581                 }
29582             ]
29583         });
29584
29585         var close = previewEl.select('button.close', true).first();
29586
29587         close.on('click', this.onRemove, this, file);
29588
29589         file.target = previewEl;
29590
29591         var image = previewEl.select('img', true).first();
29592         
29593         var _this = this;
29594         
29595         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29596         
29597         image.on('click', this.onClick, this, file);
29598         
29599         this.fireEvent('previewrendered', this, file);
29600         
29601         return file;
29602         
29603     },
29604     
29605     onPreviewLoad : function(file, image)
29606     {
29607         if(typeof(file.target) == 'undefined' || !file.target){
29608             return;
29609         }
29610         
29611         var width = image.dom.naturalWidth || image.dom.width;
29612         var height = image.dom.naturalHeight || image.dom.height;
29613         
29614         if(!this.previewResize) {
29615             return;
29616         }
29617         
29618         if(width > height){
29619             file.target.addClass('wide');
29620             return;
29621         }
29622         
29623         file.target.addClass('tall');
29624         return;
29625         
29626     },
29627     
29628     uploadFromSource : function(file, crop)
29629     {
29630         this.xhr = new XMLHttpRequest();
29631         
29632         this.managerEl.createChild({
29633             tag : 'div',
29634             cls : 'roo-document-manager-loading',
29635             cn : [
29636                 {
29637                     tag : 'div',
29638                     tooltip : file.name,
29639                     cls : 'roo-document-manager-thumb',
29640                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29641                 }
29642             ]
29643
29644         });
29645
29646         this.xhr.open(this.method, this.url, true);
29647         
29648         var headers = {
29649             "Accept": "application/json",
29650             "Cache-Control": "no-cache",
29651             "X-Requested-With": "XMLHttpRequest"
29652         };
29653         
29654         for (var headerName in headers) {
29655             var headerValue = headers[headerName];
29656             if (headerValue) {
29657                 this.xhr.setRequestHeader(headerName, headerValue);
29658             }
29659         }
29660         
29661         var _this = this;
29662         
29663         this.xhr.onload = function()
29664         {
29665             _this.xhrOnLoad(_this.xhr);
29666         }
29667         
29668         this.xhr.onerror = function()
29669         {
29670             _this.xhrOnError(_this.xhr);
29671         }
29672         
29673         var formData = new FormData();
29674
29675         formData.append('returnHTML', 'NO');
29676         
29677         formData.append('crop', crop);
29678         
29679         if(typeof(file.filename) != 'undefined'){
29680             formData.append('filename', file.filename);
29681         }
29682         
29683         if(typeof(file.mimetype) != 'undefined'){
29684             formData.append('mimetype', file.mimetype);
29685         }
29686         
29687         Roo.log(formData);
29688         
29689         if(this.fireEvent('prepare', this, formData) != false){
29690             this.xhr.send(formData);
29691         };
29692     }
29693 });
29694
29695 /*
29696 * Licence: LGPL
29697 */
29698
29699 /**
29700  * @class Roo.bootstrap.DocumentViewer
29701  * @extends Roo.bootstrap.Component
29702  * Bootstrap DocumentViewer class
29703  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29704  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29705  * 
29706  * @constructor
29707  * Create a new DocumentViewer
29708  * @param {Object} config The config object
29709  */
29710
29711 Roo.bootstrap.DocumentViewer = function(config){
29712     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29713     
29714     this.addEvents({
29715         /**
29716          * @event initial
29717          * Fire after initEvent
29718          * @param {Roo.bootstrap.DocumentViewer} this
29719          */
29720         "initial" : true,
29721         /**
29722          * @event click
29723          * Fire after click
29724          * @param {Roo.bootstrap.DocumentViewer} this
29725          */
29726         "click" : true,
29727         /**
29728          * @event download
29729          * Fire after download button
29730          * @param {Roo.bootstrap.DocumentViewer} this
29731          */
29732         "download" : true,
29733         /**
29734          * @event trash
29735          * Fire after trash button
29736          * @param {Roo.bootstrap.DocumentViewer} this
29737          */
29738         "trash" : true
29739         
29740     });
29741 };
29742
29743 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29744     
29745     showDownload : true,
29746     
29747     showTrash : true,
29748     
29749     getAutoCreate : function()
29750     {
29751         var cfg = {
29752             tag : 'div',
29753             cls : 'roo-document-viewer',
29754             cn : [
29755                 {
29756                     tag : 'div',
29757                     cls : 'roo-document-viewer-body',
29758                     cn : [
29759                         {
29760                             tag : 'div',
29761                             cls : 'roo-document-viewer-thumb',
29762                             cn : [
29763                                 {
29764                                     tag : 'img',
29765                                     cls : 'roo-document-viewer-image'
29766                                 }
29767                             ]
29768                         }
29769                     ]
29770                 },
29771                 {
29772                     tag : 'div',
29773                     cls : 'roo-document-viewer-footer',
29774                     cn : {
29775                         tag : 'div',
29776                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29777                         cn : [
29778                             {
29779                                 tag : 'div',
29780                                 cls : 'btn-group roo-document-viewer-download',
29781                                 cn : [
29782                                     {
29783                                         tag : 'button',
29784                                         cls : 'btn btn-default',
29785                                         html : '<i class="fa fa-download"></i>'
29786                                     }
29787                                 ]
29788                             },
29789                             {
29790                                 tag : 'div',
29791                                 cls : 'btn-group roo-document-viewer-trash',
29792                                 cn : [
29793                                     {
29794                                         tag : 'button',
29795                                         cls : 'btn btn-default',
29796                                         html : '<i class="fa fa-trash"></i>'
29797                                     }
29798                                 ]
29799                             }
29800                         ]
29801                     }
29802                 }
29803             ]
29804         };
29805         
29806         return cfg;
29807     },
29808     
29809     initEvents : function()
29810     {
29811         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29812         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29813         
29814         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29815         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29816         
29817         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29818         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29819         
29820         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29821         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29822         
29823         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29824         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29825         
29826         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29827         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29828         
29829         this.bodyEl.on('click', this.onClick, this);
29830         this.downloadBtn.on('click', this.onDownload, this);
29831         this.trashBtn.on('click', this.onTrash, this);
29832         
29833         this.downloadBtn.hide();
29834         this.trashBtn.hide();
29835         
29836         if(this.showDownload){
29837             this.downloadBtn.show();
29838         }
29839         
29840         if(this.showTrash){
29841             this.trashBtn.show();
29842         }
29843         
29844         if(!this.showDownload && !this.showTrash) {
29845             this.footerEl.hide();
29846         }
29847         
29848     },
29849     
29850     initial : function()
29851     {
29852         this.fireEvent('initial', this);
29853         
29854     },
29855     
29856     onClick : function(e)
29857     {
29858         e.preventDefault();
29859         
29860         this.fireEvent('click', this);
29861     },
29862     
29863     onDownload : function(e)
29864     {
29865         e.preventDefault();
29866         
29867         this.fireEvent('download', this);
29868     },
29869     
29870     onTrash : function(e)
29871     {
29872         e.preventDefault();
29873         
29874         this.fireEvent('trash', this);
29875     }
29876     
29877 });
29878 /*
29879  * - LGPL
29880  *
29881  * nav progress bar
29882  * 
29883  */
29884
29885 /**
29886  * @class Roo.bootstrap.NavProgressBar
29887  * @extends Roo.bootstrap.Component
29888  * Bootstrap NavProgressBar class
29889  * 
29890  * @constructor
29891  * Create a new nav progress bar
29892  * @param {Object} config The config object
29893  */
29894
29895 Roo.bootstrap.NavProgressBar = function(config){
29896     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29897
29898     this.bullets = this.bullets || [];
29899    
29900 //    Roo.bootstrap.NavProgressBar.register(this);
29901      this.addEvents({
29902         /**
29903              * @event changed
29904              * Fires when the active item changes
29905              * @param {Roo.bootstrap.NavProgressBar} this
29906              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29907              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29908          */
29909         'changed': true
29910      });
29911     
29912 };
29913
29914 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29915     
29916     bullets : [],
29917     barItems : [],
29918     
29919     getAutoCreate : function()
29920     {
29921         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29922         
29923         cfg = {
29924             tag : 'div',
29925             cls : 'roo-navigation-bar-group',
29926             cn : [
29927                 {
29928                     tag : 'div',
29929                     cls : 'roo-navigation-top-bar'
29930                 },
29931                 {
29932                     tag : 'div',
29933                     cls : 'roo-navigation-bullets-bar',
29934                     cn : [
29935                         {
29936                             tag : 'ul',
29937                             cls : 'roo-navigation-bar'
29938                         }
29939                     ]
29940                 },
29941                 
29942                 {
29943                     tag : 'div',
29944                     cls : 'roo-navigation-bottom-bar'
29945                 }
29946             ]
29947             
29948         };
29949         
29950         return cfg;
29951         
29952     },
29953     
29954     initEvents: function() 
29955     {
29956         
29957     },
29958     
29959     onRender : function(ct, position) 
29960     {
29961         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29962         
29963         if(this.bullets.length){
29964             Roo.each(this.bullets, function(b){
29965                this.addItem(b);
29966             }, this);
29967         }
29968         
29969         this.format();
29970         
29971     },
29972     
29973     addItem : function(cfg)
29974     {
29975         var item = new Roo.bootstrap.NavProgressItem(cfg);
29976         
29977         item.parentId = this.id;
29978         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29979         
29980         if(cfg.html){
29981             var top = new Roo.bootstrap.Element({
29982                 tag : 'div',
29983                 cls : 'roo-navigation-bar-text'
29984             });
29985             
29986             var bottom = new Roo.bootstrap.Element({
29987                 tag : 'div',
29988                 cls : 'roo-navigation-bar-text'
29989             });
29990             
29991             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29992             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29993             
29994             var topText = new Roo.bootstrap.Element({
29995                 tag : 'span',
29996                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29997             });
29998             
29999             var bottomText = new Roo.bootstrap.Element({
30000                 tag : 'span',
30001                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30002             });
30003             
30004             topText.onRender(top.el, null);
30005             bottomText.onRender(bottom.el, null);
30006             
30007             item.topEl = top;
30008             item.bottomEl = bottom;
30009         }
30010         
30011         this.barItems.push(item);
30012         
30013         return item;
30014     },
30015     
30016     getActive : function()
30017     {
30018         var active = false;
30019         
30020         Roo.each(this.barItems, function(v){
30021             
30022             if (!v.isActive()) {
30023                 return;
30024             }
30025             
30026             active = v;
30027             return false;
30028             
30029         });
30030         
30031         return active;
30032     },
30033     
30034     setActiveItem : function(item)
30035     {
30036         var prev = false;
30037         
30038         Roo.each(this.barItems, function(v){
30039             if (v.rid == item.rid) {
30040                 return ;
30041             }
30042             
30043             if (v.isActive()) {
30044                 v.setActive(false);
30045                 prev = v;
30046             }
30047         });
30048
30049         item.setActive(true);
30050         
30051         this.fireEvent('changed', this, item, prev);
30052     },
30053     
30054     getBarItem: function(rid)
30055     {
30056         var ret = false;
30057         
30058         Roo.each(this.barItems, function(e) {
30059             if (e.rid != rid) {
30060                 return;
30061             }
30062             
30063             ret =  e;
30064             return false;
30065         });
30066         
30067         return ret;
30068     },
30069     
30070     indexOfItem : function(item)
30071     {
30072         var index = false;
30073         
30074         Roo.each(this.barItems, function(v, i){
30075             
30076             if (v.rid != item.rid) {
30077                 return;
30078             }
30079             
30080             index = i;
30081             return false
30082         });
30083         
30084         return index;
30085     },
30086     
30087     setActiveNext : function()
30088     {
30089         var i = this.indexOfItem(this.getActive());
30090         
30091         if (i > this.barItems.length) {
30092             return;
30093         }
30094         
30095         this.setActiveItem(this.barItems[i+1]);
30096     },
30097     
30098     setActivePrev : function()
30099     {
30100         var i = this.indexOfItem(this.getActive());
30101         
30102         if (i  < 1) {
30103             return;
30104         }
30105         
30106         this.setActiveItem(this.barItems[i-1]);
30107     },
30108     
30109     format : function()
30110     {
30111         if(!this.barItems.length){
30112             return;
30113         }
30114      
30115         var width = 100 / this.barItems.length;
30116         
30117         Roo.each(this.barItems, function(i){
30118             i.el.setStyle('width', width + '%');
30119             i.topEl.el.setStyle('width', width + '%');
30120             i.bottomEl.el.setStyle('width', width + '%');
30121         }, this);
30122         
30123     }
30124     
30125 });
30126 /*
30127  * - LGPL
30128  *
30129  * Nav Progress Item
30130  * 
30131  */
30132
30133 /**
30134  * @class Roo.bootstrap.NavProgressItem
30135  * @extends Roo.bootstrap.Component
30136  * Bootstrap NavProgressItem class
30137  * @cfg {String} rid the reference id
30138  * @cfg {Boolean} active (true|false) Is item active default false
30139  * @cfg {Boolean} disabled (true|false) Is item active default false
30140  * @cfg {String} html
30141  * @cfg {String} position (top|bottom) text position default bottom
30142  * @cfg {String} icon show icon instead of number
30143  * 
30144  * @constructor
30145  * Create a new NavProgressItem
30146  * @param {Object} config The config object
30147  */
30148 Roo.bootstrap.NavProgressItem = function(config){
30149     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30150     this.addEvents({
30151         // raw events
30152         /**
30153          * @event click
30154          * The raw click event for the entire grid.
30155          * @param {Roo.bootstrap.NavProgressItem} this
30156          * @param {Roo.EventObject} e
30157          */
30158         "click" : true
30159     });
30160    
30161 };
30162
30163 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30164     
30165     rid : '',
30166     active : false,
30167     disabled : false,
30168     html : '',
30169     position : 'bottom',
30170     icon : false,
30171     
30172     getAutoCreate : function()
30173     {
30174         var iconCls = 'roo-navigation-bar-item-icon';
30175         
30176         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30177         
30178         var cfg = {
30179             tag: 'li',
30180             cls: 'roo-navigation-bar-item',
30181             cn : [
30182                 {
30183                     tag : 'i',
30184                     cls : iconCls
30185                 }
30186             ]
30187         };
30188         
30189         if(this.active){
30190             cfg.cls += ' active';
30191         }
30192         if(this.disabled){
30193             cfg.cls += ' disabled';
30194         }
30195         
30196         return cfg;
30197     },
30198     
30199     disable : function()
30200     {
30201         this.setDisabled(true);
30202     },
30203     
30204     enable : function()
30205     {
30206         this.setDisabled(false);
30207     },
30208     
30209     initEvents: function() 
30210     {
30211         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30212         
30213         this.iconEl.on('click', this.onClick, this);
30214     },
30215     
30216     onClick : function(e)
30217     {
30218         e.preventDefault();
30219         
30220         if(this.disabled){
30221             return;
30222         }
30223         
30224         if(this.fireEvent('click', this, e) === false){
30225             return;
30226         };
30227         
30228         this.parent().setActiveItem(this);
30229     },
30230     
30231     isActive: function () 
30232     {
30233         return this.active;
30234     },
30235     
30236     setActive : function(state)
30237     {
30238         if(this.active == state){
30239             return;
30240         }
30241         
30242         this.active = state;
30243         
30244         if (state) {
30245             this.el.addClass('active');
30246             return;
30247         }
30248         
30249         this.el.removeClass('active');
30250         
30251         return;
30252     },
30253     
30254     setDisabled : function(state)
30255     {
30256         if(this.disabled == state){
30257             return;
30258         }
30259         
30260         this.disabled = state;
30261         
30262         if (state) {
30263             this.el.addClass('disabled');
30264             return;
30265         }
30266         
30267         this.el.removeClass('disabled');
30268     },
30269     
30270     tooltipEl : function()
30271     {
30272         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30273     }
30274 });
30275  
30276
30277  /*
30278  * - LGPL
30279  *
30280  * FieldLabel
30281  * 
30282  */
30283
30284 /**
30285  * @class Roo.bootstrap.FieldLabel
30286  * @extends Roo.bootstrap.Component
30287  * Bootstrap FieldLabel class
30288  * @cfg {String} html contents of the element
30289  * @cfg {String} tag tag of the element default label
30290  * @cfg {String} cls class of the element
30291  * @cfg {String} target label target 
30292  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30293  * @cfg {String} invalidClass default "text-warning"
30294  * @cfg {String} validClass default "text-success"
30295  * @cfg {String} iconTooltip default "This field is required"
30296  * @cfg {String} indicatorpos (left|right) default left
30297  * 
30298  * @constructor
30299  * Create a new FieldLabel
30300  * @param {Object} config The config object
30301  */
30302
30303 Roo.bootstrap.FieldLabel = function(config){
30304     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30305     
30306     this.addEvents({
30307             /**
30308              * @event invalid
30309              * Fires after the field has been marked as invalid.
30310              * @param {Roo.form.FieldLabel} this
30311              * @param {String} msg The validation message
30312              */
30313             invalid : true,
30314             /**
30315              * @event valid
30316              * Fires after the field has been validated with no errors.
30317              * @param {Roo.form.FieldLabel} this
30318              */
30319             valid : true
30320         });
30321 };
30322
30323 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30324     
30325     tag: 'label',
30326     cls: '',
30327     html: '',
30328     target: '',
30329     allowBlank : true,
30330     invalidClass : 'has-warning',
30331     validClass : 'has-success',
30332     iconTooltip : 'This field is required',
30333     indicatorpos : 'left',
30334     
30335     getAutoCreate : function(){
30336         
30337         var cls = "";
30338         if (!this.allowBlank) {
30339             cls  = "visible";
30340         }
30341         
30342         var cfg = {
30343             tag : this.tag,
30344             cls : 'roo-bootstrap-field-label ' + this.cls,
30345             for : this.target,
30346             cn : [
30347                 {
30348                     tag : 'i',
30349                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30350                     tooltip : this.iconTooltip
30351                 },
30352                 {
30353                     tag : 'span',
30354                     html : this.html
30355                 }
30356             ] 
30357         };
30358         
30359         if(this.indicatorpos == 'right'){
30360             var cfg = {
30361                 tag : this.tag,
30362                 cls : 'roo-bootstrap-field-label ' + this.cls,
30363                 for : this.target,
30364                 cn : [
30365                     {
30366                         tag : 'span',
30367                         html : this.html
30368                     },
30369                     {
30370                         tag : 'i',
30371                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30372                         tooltip : this.iconTooltip
30373                     }
30374                 ] 
30375             };
30376         }
30377         
30378         return cfg;
30379     },
30380     
30381     initEvents: function() 
30382     {
30383         Roo.bootstrap.Element.superclass.initEvents.call(this);
30384         
30385         this.indicator = this.indicatorEl();
30386         
30387         if(this.indicator){
30388             this.indicator.removeClass('visible');
30389             this.indicator.addClass('invisible');
30390         }
30391         
30392         Roo.bootstrap.FieldLabel.register(this);
30393     },
30394     
30395     indicatorEl : function()
30396     {
30397         var indicator = this.el.select('i.roo-required-indicator',true).first();
30398         
30399         if(!indicator){
30400             return false;
30401         }
30402         
30403         return indicator;
30404         
30405     },
30406     
30407     /**
30408      * Mark this field as valid
30409      */
30410     markValid : function()
30411     {
30412         if(this.indicator){
30413             this.indicator.removeClass('visible');
30414             this.indicator.addClass('invisible');
30415         }
30416         
30417         this.el.removeClass(this.invalidClass);
30418         
30419         this.el.addClass(this.validClass);
30420         
30421         this.fireEvent('valid', this);
30422     },
30423     
30424     /**
30425      * Mark this field as invalid
30426      * @param {String} msg The validation message
30427      */
30428     markInvalid : function(msg)
30429     {
30430         if(this.indicator){
30431             this.indicator.removeClass('invisible');
30432             this.indicator.addClass('visible');
30433         }
30434         
30435         this.el.removeClass(this.validClass);
30436         
30437         this.el.addClass(this.invalidClass);
30438         
30439         this.fireEvent('invalid', this, msg);
30440     }
30441     
30442    
30443 });
30444
30445 Roo.apply(Roo.bootstrap.FieldLabel, {
30446     
30447     groups: {},
30448     
30449      /**
30450     * register a FieldLabel Group
30451     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30452     */
30453     register : function(label)
30454     {
30455         if(this.groups.hasOwnProperty(label.target)){
30456             return;
30457         }
30458      
30459         this.groups[label.target] = label;
30460         
30461     },
30462     /**
30463     * fetch a FieldLabel Group based on the target
30464     * @param {string} target
30465     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30466     */
30467     get: function(target) {
30468         if (typeof(this.groups[target]) == 'undefined') {
30469             return false;
30470         }
30471         
30472         return this.groups[target] ;
30473     }
30474 });
30475
30476  
30477
30478  /*
30479  * - LGPL
30480  *
30481  * page DateSplitField.
30482  * 
30483  */
30484
30485
30486 /**
30487  * @class Roo.bootstrap.DateSplitField
30488  * @extends Roo.bootstrap.Component
30489  * Bootstrap DateSplitField class
30490  * @cfg {string} fieldLabel - the label associated
30491  * @cfg {Number} labelWidth set the width of label (0-12)
30492  * @cfg {String} labelAlign (top|left)
30493  * @cfg {Boolean} dayAllowBlank (true|false) default false
30494  * @cfg {Boolean} monthAllowBlank (true|false) default false
30495  * @cfg {Boolean} yearAllowBlank (true|false) default false
30496  * @cfg {string} dayPlaceholder 
30497  * @cfg {string} monthPlaceholder
30498  * @cfg {string} yearPlaceholder
30499  * @cfg {string} dayFormat default 'd'
30500  * @cfg {string} monthFormat default 'm'
30501  * @cfg {string} yearFormat default 'Y'
30502  * @cfg {Number} labellg set the width of label (1-12)
30503  * @cfg {Number} labelmd set the width of label (1-12)
30504  * @cfg {Number} labelsm set the width of label (1-12)
30505  * @cfg {Number} labelxs set the width of label (1-12)
30506
30507  *     
30508  * @constructor
30509  * Create a new DateSplitField
30510  * @param {Object} config The config object
30511  */
30512
30513 Roo.bootstrap.DateSplitField = function(config){
30514     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30515     
30516     this.addEvents({
30517         // raw events
30518          /**
30519          * @event years
30520          * getting the data of years
30521          * @param {Roo.bootstrap.DateSplitField} this
30522          * @param {Object} years
30523          */
30524         "years" : true,
30525         /**
30526          * @event days
30527          * getting the data of days
30528          * @param {Roo.bootstrap.DateSplitField} this
30529          * @param {Object} days
30530          */
30531         "days" : true,
30532         /**
30533          * @event invalid
30534          * Fires after the field has been marked as invalid.
30535          * @param {Roo.form.Field} this
30536          * @param {String} msg The validation message
30537          */
30538         invalid : true,
30539        /**
30540          * @event valid
30541          * Fires after the field has been validated with no errors.
30542          * @param {Roo.form.Field} this
30543          */
30544         valid : true
30545     });
30546 };
30547
30548 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30549     
30550     fieldLabel : '',
30551     labelAlign : 'top',
30552     labelWidth : 3,
30553     dayAllowBlank : false,
30554     monthAllowBlank : false,
30555     yearAllowBlank : false,
30556     dayPlaceholder : '',
30557     monthPlaceholder : '',
30558     yearPlaceholder : '',
30559     dayFormat : 'd',
30560     monthFormat : 'm',
30561     yearFormat : 'Y',
30562     isFormField : true,
30563     labellg : 0,
30564     labelmd : 0,
30565     labelsm : 0,
30566     labelxs : 0,
30567     
30568     getAutoCreate : function()
30569     {
30570         var cfg = {
30571             tag : 'div',
30572             cls : 'row roo-date-split-field-group',
30573             cn : [
30574                 {
30575                     tag : 'input',
30576                     type : 'hidden',
30577                     cls : 'form-hidden-field roo-date-split-field-group-value',
30578                     name : this.name
30579                 }
30580             ]
30581         };
30582         
30583         var labelCls = 'col-md-12';
30584         var contentCls = 'col-md-4';
30585         
30586         if(this.fieldLabel){
30587             
30588             var label = {
30589                 tag : 'div',
30590                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30591                 cn : [
30592                     {
30593                         tag : 'label',
30594                         html : this.fieldLabel
30595                     }
30596                 ]
30597             };
30598             
30599             if(this.labelAlign == 'left'){
30600             
30601                 if(this.labelWidth > 12){
30602                     label.style = "width: " + this.labelWidth + 'px';
30603                 }
30604
30605                 if(this.labelWidth < 13 && this.labelmd == 0){
30606                     this.labelmd = this.labelWidth;
30607                 }
30608
30609                 if(this.labellg > 0){
30610                     labelCls = ' col-lg-' + this.labellg;
30611                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30612                 }
30613
30614                 if(this.labelmd > 0){
30615                     labelCls = ' col-md-' + this.labelmd;
30616                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30617                 }
30618
30619                 if(this.labelsm > 0){
30620                     labelCls = ' col-sm-' + this.labelsm;
30621                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30622                 }
30623
30624                 if(this.labelxs > 0){
30625                     labelCls = ' col-xs-' + this.labelxs;
30626                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30627                 }
30628             }
30629             
30630             label.cls += ' ' + labelCls;
30631             
30632             cfg.cn.push(label);
30633         }
30634         
30635         Roo.each(['day', 'month', 'year'], function(t){
30636             cfg.cn.push({
30637                 tag : 'div',
30638                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30639             });
30640         }, this);
30641         
30642         return cfg;
30643     },
30644     
30645     inputEl: function ()
30646     {
30647         return this.el.select('.roo-date-split-field-group-value', true).first();
30648     },
30649     
30650     onRender : function(ct, position) 
30651     {
30652         var _this = this;
30653         
30654         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30655         
30656         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30657         
30658         this.dayField = new Roo.bootstrap.ComboBox({
30659             allowBlank : this.dayAllowBlank,
30660             alwaysQuery : true,
30661             displayField : 'value',
30662             editable : false,
30663             fieldLabel : '',
30664             forceSelection : true,
30665             mode : 'local',
30666             placeholder : this.dayPlaceholder,
30667             selectOnFocus : true,
30668             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30669             triggerAction : 'all',
30670             typeAhead : true,
30671             valueField : 'value',
30672             store : new Roo.data.SimpleStore({
30673                 data : (function() {    
30674                     var days = [];
30675                     _this.fireEvent('days', _this, days);
30676                     return days;
30677                 })(),
30678                 fields : [ 'value' ]
30679             }),
30680             listeners : {
30681                 select : function (_self, record, index)
30682                 {
30683                     _this.setValue(_this.getValue());
30684                 }
30685             }
30686         });
30687
30688         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30689         
30690         this.monthField = new Roo.bootstrap.MonthField({
30691             after : '<i class=\"fa fa-calendar\"></i>',
30692             allowBlank : this.monthAllowBlank,
30693             placeholder : this.monthPlaceholder,
30694             readOnly : true,
30695             listeners : {
30696                 render : function (_self)
30697                 {
30698                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30699                         e.preventDefault();
30700                         _self.focus();
30701                     });
30702                 },
30703                 select : function (_self, oldvalue, newvalue)
30704                 {
30705                     _this.setValue(_this.getValue());
30706                 }
30707             }
30708         });
30709         
30710         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30711         
30712         this.yearField = new Roo.bootstrap.ComboBox({
30713             allowBlank : this.yearAllowBlank,
30714             alwaysQuery : true,
30715             displayField : 'value',
30716             editable : false,
30717             fieldLabel : '',
30718             forceSelection : true,
30719             mode : 'local',
30720             placeholder : this.yearPlaceholder,
30721             selectOnFocus : true,
30722             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30723             triggerAction : 'all',
30724             typeAhead : true,
30725             valueField : 'value',
30726             store : new Roo.data.SimpleStore({
30727                 data : (function() {
30728                     var years = [];
30729                     _this.fireEvent('years', _this, years);
30730                     return years;
30731                 })(),
30732                 fields : [ 'value' ]
30733             }),
30734             listeners : {
30735                 select : function (_self, record, index)
30736                 {
30737                     _this.setValue(_this.getValue());
30738                 }
30739             }
30740         });
30741
30742         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30743     },
30744     
30745     setValue : function(v, format)
30746     {
30747         this.inputEl.dom.value = v;
30748         
30749         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30750         
30751         var d = Date.parseDate(v, f);
30752         
30753         if(!d){
30754             this.validate();
30755             return;
30756         }
30757         
30758         this.setDay(d.format(this.dayFormat));
30759         this.setMonth(d.format(this.monthFormat));
30760         this.setYear(d.format(this.yearFormat));
30761         
30762         this.validate();
30763         
30764         return;
30765     },
30766     
30767     setDay : function(v)
30768     {
30769         this.dayField.setValue(v);
30770         this.inputEl.dom.value = this.getValue();
30771         this.validate();
30772         return;
30773     },
30774     
30775     setMonth : function(v)
30776     {
30777         this.monthField.setValue(v, true);
30778         this.inputEl.dom.value = this.getValue();
30779         this.validate();
30780         return;
30781     },
30782     
30783     setYear : function(v)
30784     {
30785         this.yearField.setValue(v);
30786         this.inputEl.dom.value = this.getValue();
30787         this.validate();
30788         return;
30789     },
30790     
30791     getDay : function()
30792     {
30793         return this.dayField.getValue();
30794     },
30795     
30796     getMonth : function()
30797     {
30798         return this.monthField.getValue();
30799     },
30800     
30801     getYear : function()
30802     {
30803         return this.yearField.getValue();
30804     },
30805     
30806     getValue : function()
30807     {
30808         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30809         
30810         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30811         
30812         return date;
30813     },
30814     
30815     reset : function()
30816     {
30817         this.setDay('');
30818         this.setMonth('');
30819         this.setYear('');
30820         this.inputEl.dom.value = '';
30821         this.validate();
30822         return;
30823     },
30824     
30825     validate : function()
30826     {
30827         var d = this.dayField.validate();
30828         var m = this.monthField.validate();
30829         var y = this.yearField.validate();
30830         
30831         var valid = true;
30832         
30833         if(
30834                 (!this.dayAllowBlank && !d) ||
30835                 (!this.monthAllowBlank && !m) ||
30836                 (!this.yearAllowBlank && !y)
30837         ){
30838             valid = false;
30839         }
30840         
30841         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30842             return valid;
30843         }
30844         
30845         if(valid){
30846             this.markValid();
30847             return valid;
30848         }
30849         
30850         this.markInvalid();
30851         
30852         return valid;
30853     },
30854     
30855     markValid : function()
30856     {
30857         
30858         var label = this.el.select('label', true).first();
30859         var icon = this.el.select('i.fa-star', true).first();
30860
30861         if(label && icon){
30862             icon.remove();
30863         }
30864         
30865         this.fireEvent('valid', this);
30866     },
30867     
30868      /**
30869      * Mark this field as invalid
30870      * @param {String} msg The validation message
30871      */
30872     markInvalid : function(msg)
30873     {
30874         
30875         var label = this.el.select('label', true).first();
30876         var icon = this.el.select('i.fa-star', true).first();
30877
30878         if(label && !icon){
30879             this.el.select('.roo-date-split-field-label', true).createChild({
30880                 tag : 'i',
30881                 cls : 'text-danger fa fa-lg fa-star',
30882                 tooltip : 'This field is required',
30883                 style : 'margin-right:5px;'
30884             }, label, true);
30885         }
30886         
30887         this.fireEvent('invalid', this, msg);
30888     },
30889     
30890     clearInvalid : function()
30891     {
30892         var label = this.el.select('label', true).first();
30893         var icon = this.el.select('i.fa-star', true).first();
30894
30895         if(label && icon){
30896             icon.remove();
30897         }
30898         
30899         this.fireEvent('valid', this);
30900     },
30901     
30902     getName: function()
30903     {
30904         return this.name;
30905     }
30906     
30907 });
30908
30909  /**
30910  *
30911  * This is based on 
30912  * http://masonry.desandro.com
30913  *
30914  * The idea is to render all the bricks based on vertical width...
30915  *
30916  * The original code extends 'outlayer' - we might need to use that....
30917  * 
30918  */
30919
30920
30921 /**
30922  * @class Roo.bootstrap.LayoutMasonry
30923  * @extends Roo.bootstrap.Component
30924  * Bootstrap Layout Masonry class
30925  * 
30926  * @constructor
30927  * Create a new Element
30928  * @param {Object} config The config object
30929  */
30930
30931 Roo.bootstrap.LayoutMasonry = function(config){
30932     
30933     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30934     
30935     this.bricks = [];
30936     
30937     Roo.bootstrap.LayoutMasonry.register(this);
30938     
30939     this.addEvents({
30940         // raw events
30941         /**
30942          * @event layout
30943          * Fire after layout the items
30944          * @param {Roo.bootstrap.LayoutMasonry} this
30945          * @param {Roo.EventObject} e
30946          */
30947         "layout" : true
30948     });
30949     
30950 };
30951
30952 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30953     
30954     /**
30955      * @cfg {Boolean} isLayoutInstant = no animation?
30956      */   
30957     isLayoutInstant : false, // needed?
30958    
30959     /**
30960      * @cfg {Number} boxWidth  width of the columns
30961      */   
30962     boxWidth : 450,
30963     
30964       /**
30965      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30966      */   
30967     boxHeight : 0,
30968     
30969     /**
30970      * @cfg {Number} padWidth padding below box..
30971      */   
30972     padWidth : 10, 
30973     
30974     /**
30975      * @cfg {Number} gutter gutter width..
30976      */   
30977     gutter : 10,
30978     
30979      /**
30980      * @cfg {Number} maxCols maximum number of columns
30981      */   
30982     
30983     maxCols: 0,
30984     
30985     /**
30986      * @cfg {Boolean} isAutoInitial defalut true
30987      */   
30988     isAutoInitial : true, 
30989     
30990     containerWidth: 0,
30991     
30992     /**
30993      * @cfg {Boolean} isHorizontal defalut false
30994      */   
30995     isHorizontal : false, 
30996
30997     currentSize : null,
30998     
30999     tag: 'div',
31000     
31001     cls: '',
31002     
31003     bricks: null, //CompositeElement
31004     
31005     cols : 1,
31006     
31007     _isLayoutInited : false,
31008     
31009 //    isAlternative : false, // only use for vertical layout...
31010     
31011     /**
31012      * @cfg {Number} alternativePadWidth padding below box..
31013      */   
31014     alternativePadWidth : 50,
31015     
31016     selectedBrick : [],
31017     
31018     getAutoCreate : function(){
31019         
31020         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31021         
31022         var cfg = {
31023             tag: this.tag,
31024             cls: 'blog-masonary-wrapper ' + this.cls,
31025             cn : {
31026                 cls : 'mas-boxes masonary'
31027             }
31028         };
31029         
31030         return cfg;
31031     },
31032     
31033     getChildContainer: function( )
31034     {
31035         if (this.boxesEl) {
31036             return this.boxesEl;
31037         }
31038         
31039         this.boxesEl = this.el.select('.mas-boxes').first();
31040         
31041         return this.boxesEl;
31042     },
31043     
31044     
31045     initEvents : function()
31046     {
31047         var _this = this;
31048         
31049         if(this.isAutoInitial){
31050             Roo.log('hook children rendered');
31051             this.on('childrenrendered', function() {
31052                 Roo.log('children rendered');
31053                 _this.initial();
31054             } ,this);
31055         }
31056     },
31057     
31058     initial : function()
31059     {
31060         this.selectedBrick = [];
31061         
31062         this.currentSize = this.el.getBox(true);
31063         
31064         Roo.EventManager.onWindowResize(this.resize, this); 
31065
31066         if(!this.isAutoInitial){
31067             this.layout();
31068             return;
31069         }
31070         
31071         this.layout();
31072         
31073         return;
31074         //this.layout.defer(500,this);
31075         
31076     },
31077     
31078     resize : function()
31079     {
31080         var cs = this.el.getBox(true);
31081         
31082         if (
31083                 this.currentSize.width == cs.width && 
31084                 this.currentSize.x == cs.x && 
31085                 this.currentSize.height == cs.height && 
31086                 this.currentSize.y == cs.y 
31087         ) {
31088             Roo.log("no change in with or X or Y");
31089             return;
31090         }
31091         
31092         this.currentSize = cs;
31093         
31094         this.layout();
31095         
31096     },
31097     
31098     layout : function()
31099     {   
31100         this._resetLayout();
31101         
31102         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31103         
31104         this.layoutItems( isInstant );
31105       
31106         this._isLayoutInited = true;
31107         
31108         this.fireEvent('layout', this);
31109         
31110     },
31111     
31112     _resetLayout : function()
31113     {
31114         if(this.isHorizontal){
31115             this.horizontalMeasureColumns();
31116             return;
31117         }
31118         
31119         this.verticalMeasureColumns();
31120         
31121     },
31122     
31123     verticalMeasureColumns : function()
31124     {
31125         this.getContainerWidth();
31126         
31127 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31128 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31129 //            return;
31130 //        }
31131         
31132         var boxWidth = this.boxWidth + this.padWidth;
31133         
31134         if(this.containerWidth < this.boxWidth){
31135             boxWidth = this.containerWidth
31136         }
31137         
31138         var containerWidth = this.containerWidth;
31139         
31140         var cols = Math.floor(containerWidth / boxWidth);
31141         
31142         this.cols = Math.max( cols, 1 );
31143         
31144         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31145         
31146         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31147         
31148         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31149         
31150         this.colWidth = boxWidth + avail - this.padWidth;
31151         
31152         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31153         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31154     },
31155     
31156     horizontalMeasureColumns : function()
31157     {
31158         this.getContainerWidth();
31159         
31160         var boxWidth = this.boxWidth;
31161         
31162         if(this.containerWidth < boxWidth){
31163             boxWidth = this.containerWidth;
31164         }
31165         
31166         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31167         
31168         this.el.setHeight(boxWidth);
31169         
31170     },
31171     
31172     getContainerWidth : function()
31173     {
31174         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31175     },
31176     
31177     layoutItems : function( isInstant )
31178     {
31179         Roo.log(this.bricks);
31180         
31181         var items = Roo.apply([], this.bricks);
31182         
31183         if(this.isHorizontal){
31184             this._horizontalLayoutItems( items , isInstant );
31185             return;
31186         }
31187         
31188 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31189 //            this._verticalAlternativeLayoutItems( items , isInstant );
31190 //            return;
31191 //        }
31192         
31193         this._verticalLayoutItems( items , isInstant );
31194         
31195     },
31196     
31197     _verticalLayoutItems : function ( items , isInstant)
31198     {
31199         if ( !items || !items.length ) {
31200             return;
31201         }
31202         
31203         var standard = [
31204             ['xs', 'xs', 'xs', 'tall'],
31205             ['xs', 'xs', 'tall'],
31206             ['xs', 'xs', 'sm'],
31207             ['xs', 'xs', 'xs'],
31208             ['xs', 'tall'],
31209             ['xs', 'sm'],
31210             ['xs', 'xs'],
31211             ['xs'],
31212             
31213             ['sm', 'xs', 'xs'],
31214             ['sm', 'xs'],
31215             ['sm'],
31216             
31217             ['tall', 'xs', 'xs', 'xs'],
31218             ['tall', 'xs', 'xs'],
31219             ['tall', 'xs'],
31220             ['tall']
31221             
31222         ];
31223         
31224         var queue = [];
31225         
31226         var boxes = [];
31227         
31228         var box = [];
31229         
31230         Roo.each(items, function(item, k){
31231             
31232             switch (item.size) {
31233                 // these layouts take up a full box,
31234                 case 'md' :
31235                 case 'md-left' :
31236                 case 'md-right' :
31237                 case 'wide' :
31238                     
31239                     if(box.length){
31240                         boxes.push(box);
31241                         box = [];
31242                     }
31243                     
31244                     boxes.push([item]);
31245                     
31246                     break;
31247                     
31248                 case 'xs' :
31249                 case 'sm' :
31250                 case 'tall' :
31251                     
31252                     box.push(item);
31253                     
31254                     break;
31255                 default :
31256                     break;
31257                     
31258             }
31259             
31260         }, this);
31261         
31262         if(box.length){
31263             boxes.push(box);
31264             box = [];
31265         }
31266         
31267         var filterPattern = function(box, length)
31268         {
31269             if(!box.length){
31270                 return;
31271             }
31272             
31273             var match = false;
31274             
31275             var pattern = box.slice(0, length);
31276             
31277             var format = [];
31278             
31279             Roo.each(pattern, function(i){
31280                 format.push(i.size);
31281             }, this);
31282             
31283             Roo.each(standard, function(s){
31284                 
31285                 if(String(s) != String(format)){
31286                     return;
31287                 }
31288                 
31289                 match = true;
31290                 return false;
31291                 
31292             }, this);
31293             
31294             if(!match && length == 1){
31295                 return;
31296             }
31297             
31298             if(!match){
31299                 filterPattern(box, length - 1);
31300                 return;
31301             }
31302                 
31303             queue.push(pattern);
31304
31305             box = box.slice(length, box.length);
31306
31307             filterPattern(box, 4);
31308
31309             return;
31310             
31311         }
31312         
31313         Roo.each(boxes, function(box, k){
31314             
31315             if(!box.length){
31316                 return;
31317             }
31318             
31319             if(box.length == 1){
31320                 queue.push(box);
31321                 return;
31322             }
31323             
31324             filterPattern(box, 4);
31325             
31326         }, this);
31327         
31328         this._processVerticalLayoutQueue( queue, isInstant );
31329         
31330     },
31331     
31332 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31333 //    {
31334 //        if ( !items || !items.length ) {
31335 //            return;
31336 //        }
31337 //
31338 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31339 //        
31340 //    },
31341     
31342     _horizontalLayoutItems : function ( items , isInstant)
31343     {
31344         if ( !items || !items.length || items.length < 3) {
31345             return;
31346         }
31347         
31348         items.reverse();
31349         
31350         var eItems = items.slice(0, 3);
31351         
31352         items = items.slice(3, items.length);
31353         
31354         var standard = [
31355             ['xs', 'xs', 'xs', 'wide'],
31356             ['xs', 'xs', 'wide'],
31357             ['xs', 'xs', 'sm'],
31358             ['xs', 'xs', 'xs'],
31359             ['xs', 'wide'],
31360             ['xs', 'sm'],
31361             ['xs', 'xs'],
31362             ['xs'],
31363             
31364             ['sm', 'xs', 'xs'],
31365             ['sm', 'xs'],
31366             ['sm'],
31367             
31368             ['wide', 'xs', 'xs', 'xs'],
31369             ['wide', 'xs', 'xs'],
31370             ['wide', 'xs'],
31371             ['wide'],
31372             
31373             ['wide-thin']
31374         ];
31375         
31376         var queue = [];
31377         
31378         var boxes = [];
31379         
31380         var box = [];
31381         
31382         Roo.each(items, function(item, k){
31383             
31384             switch (item.size) {
31385                 case 'md' :
31386                 case 'md-left' :
31387                 case 'md-right' :
31388                 case 'tall' :
31389                     
31390                     if(box.length){
31391                         boxes.push(box);
31392                         box = [];
31393                     }
31394                     
31395                     boxes.push([item]);
31396                     
31397                     break;
31398                     
31399                 case 'xs' :
31400                 case 'sm' :
31401                 case 'wide' :
31402                 case 'wide-thin' :
31403                     
31404                     box.push(item);
31405                     
31406                     break;
31407                 default :
31408                     break;
31409                     
31410             }
31411             
31412         }, this);
31413         
31414         if(box.length){
31415             boxes.push(box);
31416             box = [];
31417         }
31418         
31419         var filterPattern = function(box, length)
31420         {
31421             if(!box.length){
31422                 return;
31423             }
31424             
31425             var match = false;
31426             
31427             var pattern = box.slice(0, length);
31428             
31429             var format = [];
31430             
31431             Roo.each(pattern, function(i){
31432                 format.push(i.size);
31433             }, this);
31434             
31435             Roo.each(standard, function(s){
31436                 
31437                 if(String(s) != String(format)){
31438                     return;
31439                 }
31440                 
31441                 match = true;
31442                 return false;
31443                 
31444             }, this);
31445             
31446             if(!match && length == 1){
31447                 return;
31448             }
31449             
31450             if(!match){
31451                 filterPattern(box, length - 1);
31452                 return;
31453             }
31454                 
31455             queue.push(pattern);
31456
31457             box = box.slice(length, box.length);
31458
31459             filterPattern(box, 4);
31460
31461             return;
31462             
31463         }
31464         
31465         Roo.each(boxes, function(box, k){
31466             
31467             if(!box.length){
31468                 return;
31469             }
31470             
31471             if(box.length == 1){
31472                 queue.push(box);
31473                 return;
31474             }
31475             
31476             filterPattern(box, 4);
31477             
31478         }, this);
31479         
31480         
31481         var prune = [];
31482         
31483         var pos = this.el.getBox(true);
31484         
31485         var minX = pos.x;
31486         
31487         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31488         
31489         var hit_end = false;
31490         
31491         Roo.each(queue, function(box){
31492             
31493             if(hit_end){
31494                 
31495                 Roo.each(box, function(b){
31496                 
31497                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31498                     b.el.hide();
31499
31500                 }, this);
31501
31502                 return;
31503             }
31504             
31505             var mx = 0;
31506             
31507             Roo.each(box, function(b){
31508                 
31509                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31510                 b.el.show();
31511
31512                 mx = Math.max(mx, b.x);
31513                 
31514             }, this);
31515             
31516             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31517             
31518             if(maxX < minX){
31519                 
31520                 Roo.each(box, function(b){
31521                 
31522                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31523                     b.el.hide();
31524                     
31525                 }, this);
31526                 
31527                 hit_end = true;
31528                 
31529                 return;
31530             }
31531             
31532             prune.push(box);
31533             
31534         }, this);
31535         
31536         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31537     },
31538     
31539     /** Sets position of item in DOM
31540     * @param {Element} item
31541     * @param {Number} x - horizontal position
31542     * @param {Number} y - vertical position
31543     * @param {Boolean} isInstant - disables transitions
31544     */
31545     _processVerticalLayoutQueue : function( queue, isInstant )
31546     {
31547         var pos = this.el.getBox(true);
31548         var x = pos.x;
31549         var y = pos.y;
31550         var maxY = [];
31551         
31552         for (var i = 0; i < this.cols; i++){
31553             maxY[i] = pos.y;
31554         }
31555         
31556         Roo.each(queue, function(box, k){
31557             
31558             var col = k % this.cols;
31559             
31560             Roo.each(box, function(b,kk){
31561                 
31562                 b.el.position('absolute');
31563                 
31564                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31565                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31566                 
31567                 if(b.size == 'md-left' || b.size == 'md-right'){
31568                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31569                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31570                 }
31571                 
31572                 b.el.setWidth(width);
31573                 b.el.setHeight(height);
31574                 // iframe?
31575                 b.el.select('iframe',true).setSize(width,height);
31576                 
31577             }, this);
31578             
31579             for (var i = 0; i < this.cols; i++){
31580                 
31581                 if(maxY[i] < maxY[col]){
31582                     col = i;
31583                     continue;
31584                 }
31585                 
31586                 col = Math.min(col, i);
31587                 
31588             }
31589             
31590             x = pos.x + col * (this.colWidth + this.padWidth);
31591             
31592             y = maxY[col];
31593             
31594             var positions = [];
31595             
31596             switch (box.length){
31597                 case 1 :
31598                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31599                     break;
31600                 case 2 :
31601                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31602                     break;
31603                 case 3 :
31604                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31605                     break;
31606                 case 4 :
31607                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31608                     break;
31609                 default :
31610                     break;
31611             }
31612             
31613             Roo.each(box, function(b,kk){
31614                 
31615                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31616                 
31617                 var sz = b.el.getSize();
31618                 
31619                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31620                 
31621             }, this);
31622             
31623         }, this);
31624         
31625         var mY = 0;
31626         
31627         for (var i = 0; i < this.cols; i++){
31628             mY = Math.max(mY, maxY[i]);
31629         }
31630         
31631         this.el.setHeight(mY - pos.y);
31632         
31633     },
31634     
31635 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31636 //    {
31637 //        var pos = this.el.getBox(true);
31638 //        var x = pos.x;
31639 //        var y = pos.y;
31640 //        var maxX = pos.right;
31641 //        
31642 //        var maxHeight = 0;
31643 //        
31644 //        Roo.each(items, function(item, k){
31645 //            
31646 //            var c = k % 2;
31647 //            
31648 //            item.el.position('absolute');
31649 //                
31650 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31651 //
31652 //            item.el.setWidth(width);
31653 //
31654 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31655 //
31656 //            item.el.setHeight(height);
31657 //            
31658 //            if(c == 0){
31659 //                item.el.setXY([x, y], isInstant ? false : true);
31660 //            } else {
31661 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31662 //            }
31663 //            
31664 //            y = y + height + this.alternativePadWidth;
31665 //            
31666 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31667 //            
31668 //        }, this);
31669 //        
31670 //        this.el.setHeight(maxHeight);
31671 //        
31672 //    },
31673     
31674     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31675     {
31676         var pos = this.el.getBox(true);
31677         
31678         var minX = pos.x;
31679         var minY = pos.y;
31680         
31681         var maxX = pos.right;
31682         
31683         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31684         
31685         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31686         
31687         Roo.each(queue, function(box, k){
31688             
31689             Roo.each(box, function(b, kk){
31690                 
31691                 b.el.position('absolute');
31692                 
31693                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31694                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31695                 
31696                 if(b.size == 'md-left' || b.size == 'md-right'){
31697                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31698                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31699                 }
31700                 
31701                 b.el.setWidth(width);
31702                 b.el.setHeight(height);
31703                 
31704             }, this);
31705             
31706             if(!box.length){
31707                 return;
31708             }
31709             
31710             var positions = [];
31711             
31712             switch (box.length){
31713                 case 1 :
31714                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31715                     break;
31716                 case 2 :
31717                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31718                     break;
31719                 case 3 :
31720                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31721                     break;
31722                 case 4 :
31723                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31724                     break;
31725                 default :
31726                     break;
31727             }
31728             
31729             Roo.each(box, function(b,kk){
31730                 
31731                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31732                 
31733                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31734                 
31735             }, this);
31736             
31737         }, this);
31738         
31739     },
31740     
31741     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31742     {
31743         Roo.each(eItems, function(b,k){
31744             
31745             b.size = (k == 0) ? 'sm' : 'xs';
31746             b.x = (k == 0) ? 2 : 1;
31747             b.y = (k == 0) ? 2 : 1;
31748             
31749             b.el.position('absolute');
31750             
31751             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31752                 
31753             b.el.setWidth(width);
31754             
31755             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31756             
31757             b.el.setHeight(height);
31758             
31759         }, this);
31760
31761         var positions = [];
31762         
31763         positions.push({
31764             x : maxX - this.unitWidth * 2 - this.gutter,
31765             y : minY
31766         });
31767         
31768         positions.push({
31769             x : maxX - this.unitWidth,
31770             y : minY + (this.unitWidth + this.gutter) * 2
31771         });
31772         
31773         positions.push({
31774             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31775             y : minY
31776         });
31777         
31778         Roo.each(eItems, function(b,k){
31779             
31780             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31781
31782         }, this);
31783         
31784     },
31785     
31786     getVerticalOneBoxColPositions : function(x, y, box)
31787     {
31788         var pos = [];
31789         
31790         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31791         
31792         if(box[0].size == 'md-left'){
31793             rand = 0;
31794         }
31795         
31796         if(box[0].size == 'md-right'){
31797             rand = 1;
31798         }
31799         
31800         pos.push({
31801             x : x + (this.unitWidth + this.gutter) * rand,
31802             y : y
31803         });
31804         
31805         return pos;
31806     },
31807     
31808     getVerticalTwoBoxColPositions : function(x, y, box)
31809     {
31810         var pos = [];
31811         
31812         if(box[0].size == 'xs'){
31813             
31814             pos.push({
31815                 x : x,
31816                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31817             });
31818
31819             pos.push({
31820                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31821                 y : y
31822             });
31823             
31824             return pos;
31825             
31826         }
31827         
31828         pos.push({
31829             x : x,
31830             y : y
31831         });
31832
31833         pos.push({
31834             x : x + (this.unitWidth + this.gutter) * 2,
31835             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31836         });
31837         
31838         return pos;
31839         
31840     },
31841     
31842     getVerticalThreeBoxColPositions : function(x, y, box)
31843     {
31844         var pos = [];
31845         
31846         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31847             
31848             pos.push({
31849                 x : x,
31850                 y : y
31851             });
31852
31853             pos.push({
31854                 x : x + (this.unitWidth + this.gutter) * 1,
31855                 y : y
31856             });
31857             
31858             pos.push({
31859                 x : x + (this.unitWidth + this.gutter) * 2,
31860                 y : y
31861             });
31862             
31863             return pos;
31864             
31865         }
31866         
31867         if(box[0].size == 'xs' && box[1].size == 'xs'){
31868             
31869             pos.push({
31870                 x : x,
31871                 y : y
31872             });
31873
31874             pos.push({
31875                 x : x,
31876                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31877             });
31878             
31879             pos.push({
31880                 x : x + (this.unitWidth + this.gutter) * 1,
31881                 y : y
31882             });
31883             
31884             return pos;
31885             
31886         }
31887         
31888         pos.push({
31889             x : x,
31890             y : y
31891         });
31892
31893         pos.push({
31894             x : x + (this.unitWidth + this.gutter) * 2,
31895             y : y
31896         });
31897
31898         pos.push({
31899             x : x + (this.unitWidth + this.gutter) * 2,
31900             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31901         });
31902             
31903         return pos;
31904         
31905     },
31906     
31907     getVerticalFourBoxColPositions : function(x, y, box)
31908     {
31909         var pos = [];
31910         
31911         if(box[0].size == 'xs'){
31912             
31913             pos.push({
31914                 x : x,
31915                 y : y
31916             });
31917
31918             pos.push({
31919                 x : x,
31920                 y : y + (this.unitHeight + this.gutter) * 1
31921             });
31922             
31923             pos.push({
31924                 x : x,
31925                 y : y + (this.unitHeight + this.gutter) * 2
31926             });
31927             
31928             pos.push({
31929                 x : x + (this.unitWidth + this.gutter) * 1,
31930                 y : y
31931             });
31932             
31933             return pos;
31934             
31935         }
31936         
31937         pos.push({
31938             x : x,
31939             y : y
31940         });
31941
31942         pos.push({
31943             x : x + (this.unitWidth + this.gutter) * 2,
31944             y : y
31945         });
31946
31947         pos.push({
31948             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31949             y : y + (this.unitHeight + this.gutter) * 1
31950         });
31951
31952         pos.push({
31953             x : x + (this.unitWidth + this.gutter) * 2,
31954             y : y + (this.unitWidth + this.gutter) * 2
31955         });
31956
31957         return pos;
31958         
31959     },
31960     
31961     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31962     {
31963         var pos = [];
31964         
31965         if(box[0].size == 'md-left'){
31966             pos.push({
31967                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31968                 y : minY
31969             });
31970             
31971             return pos;
31972         }
31973         
31974         if(box[0].size == 'md-right'){
31975             pos.push({
31976                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31977                 y : minY + (this.unitWidth + this.gutter) * 1
31978             });
31979             
31980             return pos;
31981         }
31982         
31983         var rand = Math.floor(Math.random() * (4 - box[0].y));
31984         
31985         pos.push({
31986             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31987             y : minY + (this.unitWidth + this.gutter) * rand
31988         });
31989         
31990         return pos;
31991         
31992     },
31993     
31994     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31995     {
31996         var pos = [];
31997         
31998         if(box[0].size == 'xs'){
31999             
32000             pos.push({
32001                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32002                 y : minY
32003             });
32004
32005             pos.push({
32006                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32007                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32008             });
32009             
32010             return pos;
32011             
32012         }
32013         
32014         pos.push({
32015             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32016             y : minY
32017         });
32018
32019         pos.push({
32020             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32021             y : minY + (this.unitWidth + this.gutter) * 2
32022         });
32023         
32024         return pos;
32025         
32026     },
32027     
32028     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32029     {
32030         var pos = [];
32031         
32032         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32033             
32034             pos.push({
32035                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32036                 y : minY
32037             });
32038
32039             pos.push({
32040                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32041                 y : minY + (this.unitWidth + this.gutter) * 1
32042             });
32043             
32044             pos.push({
32045                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32046                 y : minY + (this.unitWidth + this.gutter) * 2
32047             });
32048             
32049             return pos;
32050             
32051         }
32052         
32053         if(box[0].size == 'xs' && box[1].size == 'xs'){
32054             
32055             pos.push({
32056                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32057                 y : minY
32058             });
32059
32060             pos.push({
32061                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32062                 y : minY
32063             });
32064             
32065             pos.push({
32066                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32067                 y : minY + (this.unitWidth + this.gutter) * 1
32068             });
32069             
32070             return pos;
32071             
32072         }
32073         
32074         pos.push({
32075             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32076             y : minY
32077         });
32078
32079         pos.push({
32080             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32081             y : minY + (this.unitWidth + this.gutter) * 2
32082         });
32083
32084         pos.push({
32085             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32086             y : minY + (this.unitWidth + this.gutter) * 2
32087         });
32088             
32089         return pos;
32090         
32091     },
32092     
32093     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32094     {
32095         var pos = [];
32096         
32097         if(box[0].size == 'xs'){
32098             
32099             pos.push({
32100                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32101                 y : minY
32102             });
32103
32104             pos.push({
32105                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32106                 y : minY
32107             });
32108             
32109             pos.push({
32110                 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),
32111                 y : minY
32112             });
32113             
32114             pos.push({
32115                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32116                 y : minY + (this.unitWidth + this.gutter) * 1
32117             });
32118             
32119             return pos;
32120             
32121         }
32122         
32123         pos.push({
32124             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32125             y : minY
32126         });
32127         
32128         pos.push({
32129             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32130             y : minY + (this.unitWidth + this.gutter) * 2
32131         });
32132         
32133         pos.push({
32134             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32135             y : minY + (this.unitWidth + this.gutter) * 2
32136         });
32137         
32138         pos.push({
32139             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),
32140             y : minY + (this.unitWidth + this.gutter) * 2
32141         });
32142
32143         return pos;
32144         
32145     },
32146     
32147     /**
32148     * remove a Masonry Brick
32149     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32150     */
32151     removeBrick : function(brick_id)
32152     {
32153         if (!brick_id) {
32154             return;
32155         }
32156         
32157         for (var i = 0; i<this.bricks.length; i++) {
32158             if (this.bricks[i].id == brick_id) {
32159                 this.bricks.splice(i,1);
32160                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32161                 this.initial();
32162             }
32163         }
32164     },
32165     
32166     /**
32167     * adds a Masonry Brick
32168     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32169     */
32170     addBrick : function(cfg)
32171     {
32172         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32173         //this.register(cn);
32174         cn.parentId = this.id;
32175         cn.render(this.el);
32176         return cn;
32177     },
32178     
32179     /**
32180     * register a Masonry Brick
32181     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32182     */
32183     
32184     register : function(brick)
32185     {
32186         this.bricks.push(brick);
32187         brick.masonryId = this.id;
32188     },
32189     
32190     /**
32191     * clear all the Masonry Brick
32192     */
32193     clearAll : function()
32194     {
32195         this.bricks = [];
32196         //this.getChildContainer().dom.innerHTML = "";
32197         this.el.dom.innerHTML = '';
32198     },
32199     
32200     getSelected : function()
32201     {
32202         if (!this.selectedBrick) {
32203             return false;
32204         }
32205         
32206         return this.selectedBrick;
32207     }
32208 });
32209
32210 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32211     
32212     groups: {},
32213      /**
32214     * register a Masonry Layout
32215     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32216     */
32217     
32218     register : function(layout)
32219     {
32220         this.groups[layout.id] = layout;
32221     },
32222     /**
32223     * fetch a  Masonry Layout based on the masonry layout ID
32224     * @param {string} the masonry layout to add
32225     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32226     */
32227     
32228     get: function(layout_id) {
32229         if (typeof(this.groups[layout_id]) == 'undefined') {
32230             return false;
32231         }
32232         return this.groups[layout_id] ;
32233     }
32234     
32235     
32236     
32237 });
32238
32239  
32240
32241  /**
32242  *
32243  * This is based on 
32244  * http://masonry.desandro.com
32245  *
32246  * The idea is to render all the bricks based on vertical width...
32247  *
32248  * The original code extends 'outlayer' - we might need to use that....
32249  * 
32250  */
32251
32252
32253 /**
32254  * @class Roo.bootstrap.LayoutMasonryAuto
32255  * @extends Roo.bootstrap.Component
32256  * Bootstrap Layout Masonry class
32257  * 
32258  * @constructor
32259  * Create a new Element
32260  * @param {Object} config The config object
32261  */
32262
32263 Roo.bootstrap.LayoutMasonryAuto = function(config){
32264     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32265 };
32266
32267 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32268     
32269       /**
32270      * @cfg {Boolean} isFitWidth  - resize the width..
32271      */   
32272     isFitWidth : false,  // options..
32273     /**
32274      * @cfg {Boolean} isOriginLeft = left align?
32275      */   
32276     isOriginLeft : true,
32277     /**
32278      * @cfg {Boolean} isOriginTop = top align?
32279      */   
32280     isOriginTop : false,
32281     /**
32282      * @cfg {Boolean} isLayoutInstant = no animation?
32283      */   
32284     isLayoutInstant : false, // needed?
32285     /**
32286      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32287      */   
32288     isResizingContainer : true,
32289     /**
32290      * @cfg {Number} columnWidth  width of the columns 
32291      */   
32292     
32293     columnWidth : 0,
32294     
32295     /**
32296      * @cfg {Number} maxCols maximum number of columns
32297      */   
32298     
32299     maxCols: 0,
32300     /**
32301      * @cfg {Number} padHeight padding below box..
32302      */   
32303     
32304     padHeight : 10, 
32305     
32306     /**
32307      * @cfg {Boolean} isAutoInitial defalut true
32308      */   
32309     
32310     isAutoInitial : true, 
32311     
32312     // private?
32313     gutter : 0,
32314     
32315     containerWidth: 0,
32316     initialColumnWidth : 0,
32317     currentSize : null,
32318     
32319     colYs : null, // array.
32320     maxY : 0,
32321     padWidth: 10,
32322     
32323     
32324     tag: 'div',
32325     cls: '',
32326     bricks: null, //CompositeElement
32327     cols : 0, // array?
32328     // element : null, // wrapped now this.el
32329     _isLayoutInited : null, 
32330     
32331     
32332     getAutoCreate : function(){
32333         
32334         var cfg = {
32335             tag: this.tag,
32336             cls: 'blog-masonary-wrapper ' + this.cls,
32337             cn : {
32338                 cls : 'mas-boxes masonary'
32339             }
32340         };
32341         
32342         return cfg;
32343     },
32344     
32345     getChildContainer: function( )
32346     {
32347         if (this.boxesEl) {
32348             return this.boxesEl;
32349         }
32350         
32351         this.boxesEl = this.el.select('.mas-boxes').first();
32352         
32353         return this.boxesEl;
32354     },
32355     
32356     
32357     initEvents : function()
32358     {
32359         var _this = this;
32360         
32361         if(this.isAutoInitial){
32362             Roo.log('hook children rendered');
32363             this.on('childrenrendered', function() {
32364                 Roo.log('children rendered');
32365                 _this.initial();
32366             } ,this);
32367         }
32368         
32369     },
32370     
32371     initial : function()
32372     {
32373         this.reloadItems();
32374
32375         this.currentSize = this.el.getBox(true);
32376
32377         /// was window resize... - let's see if this works..
32378         Roo.EventManager.onWindowResize(this.resize, this); 
32379
32380         if(!this.isAutoInitial){
32381             this.layout();
32382             return;
32383         }
32384         
32385         this.layout.defer(500,this);
32386     },
32387     
32388     reloadItems: function()
32389     {
32390         this.bricks = this.el.select('.masonry-brick', true);
32391         
32392         this.bricks.each(function(b) {
32393             //Roo.log(b.getSize());
32394             if (!b.attr('originalwidth')) {
32395                 b.attr('originalwidth',  b.getSize().width);
32396             }
32397             
32398         });
32399         
32400         Roo.log(this.bricks.elements.length);
32401     },
32402     
32403     resize : function()
32404     {
32405         Roo.log('resize');
32406         var cs = this.el.getBox(true);
32407         
32408         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32409             Roo.log("no change in with or X");
32410             return;
32411         }
32412         this.currentSize = cs;
32413         this.layout();
32414     },
32415     
32416     layout : function()
32417     {
32418          Roo.log('layout');
32419         this._resetLayout();
32420         //this._manageStamps();
32421       
32422         // don't animate first layout
32423         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32424         this.layoutItems( isInstant );
32425       
32426         // flag for initalized
32427         this._isLayoutInited = true;
32428     },
32429     
32430     layoutItems : function( isInstant )
32431     {
32432         //var items = this._getItemsForLayout( this.items );
32433         // original code supports filtering layout items.. we just ignore it..
32434         
32435         this._layoutItems( this.bricks , isInstant );
32436       
32437         this._postLayout();
32438     },
32439     _layoutItems : function ( items , isInstant)
32440     {
32441        //this.fireEvent( 'layout', this, items );
32442     
32443
32444         if ( !items || !items.elements.length ) {
32445           // no items, emit event with empty array
32446             return;
32447         }
32448
32449         var queue = [];
32450         items.each(function(item) {
32451             Roo.log("layout item");
32452             Roo.log(item);
32453             // get x/y object from method
32454             var position = this._getItemLayoutPosition( item );
32455             // enqueue
32456             position.item = item;
32457             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32458             queue.push( position );
32459         }, this);
32460       
32461         this._processLayoutQueue( queue );
32462     },
32463     /** Sets position of item in DOM
32464     * @param {Element} item
32465     * @param {Number} x - horizontal position
32466     * @param {Number} y - vertical position
32467     * @param {Boolean} isInstant - disables transitions
32468     */
32469     _processLayoutQueue : function( queue )
32470     {
32471         for ( var i=0, len = queue.length; i < len; i++ ) {
32472             var obj = queue[i];
32473             obj.item.position('absolute');
32474             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32475         }
32476     },
32477       
32478     
32479     /**
32480     * Any logic you want to do after each layout,
32481     * i.e. size the container
32482     */
32483     _postLayout : function()
32484     {
32485         this.resizeContainer();
32486     },
32487     
32488     resizeContainer : function()
32489     {
32490         if ( !this.isResizingContainer ) {
32491             return;
32492         }
32493         var size = this._getContainerSize();
32494         if ( size ) {
32495             this.el.setSize(size.width,size.height);
32496             this.boxesEl.setSize(size.width,size.height);
32497         }
32498     },
32499     
32500     
32501     
32502     _resetLayout : function()
32503     {
32504         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32505         this.colWidth = this.el.getWidth();
32506         //this.gutter = this.el.getWidth(); 
32507         
32508         this.measureColumns();
32509
32510         // reset column Y
32511         var i = this.cols;
32512         this.colYs = [];
32513         while (i--) {
32514             this.colYs.push( 0 );
32515         }
32516     
32517         this.maxY = 0;
32518     },
32519
32520     measureColumns : function()
32521     {
32522         this.getContainerWidth();
32523       // if columnWidth is 0, default to outerWidth of first item
32524         if ( !this.columnWidth ) {
32525             var firstItem = this.bricks.first();
32526             Roo.log(firstItem);
32527             this.columnWidth  = this.containerWidth;
32528             if (firstItem && firstItem.attr('originalwidth') ) {
32529                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32530             }
32531             // columnWidth fall back to item of first element
32532             Roo.log("set column width?");
32533                         this.initialColumnWidth = this.columnWidth  ;
32534
32535             // if first elem has no width, default to size of container
32536             
32537         }
32538         
32539         
32540         if (this.initialColumnWidth) {
32541             this.columnWidth = this.initialColumnWidth;
32542         }
32543         
32544         
32545             
32546         // column width is fixed at the top - however if container width get's smaller we should
32547         // reduce it...
32548         
32549         // this bit calcs how man columns..
32550             
32551         var columnWidth = this.columnWidth += this.gutter;
32552       
32553         // calculate columns
32554         var containerWidth = this.containerWidth + this.gutter;
32555         
32556         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32557         // fix rounding errors, typically with gutters
32558         var excess = columnWidth - containerWidth % columnWidth;
32559         
32560         
32561         // if overshoot is less than a pixel, round up, otherwise floor it
32562         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32563         cols = Math[ mathMethod ]( cols );
32564         this.cols = Math.max( cols, 1 );
32565         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32566         
32567          // padding positioning..
32568         var totalColWidth = this.cols * this.columnWidth;
32569         var padavail = this.containerWidth - totalColWidth;
32570         // so for 2 columns - we need 3 'pads'
32571         
32572         var padNeeded = (1+this.cols) * this.padWidth;
32573         
32574         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32575         
32576         this.columnWidth += padExtra
32577         //this.padWidth = Math.floor(padavail /  ( this.cols));
32578         
32579         // adjust colum width so that padding is fixed??
32580         
32581         // we have 3 columns ... total = width * 3
32582         // we have X left over... that should be used by 
32583         
32584         //if (this.expandC) {
32585             
32586         //}
32587         
32588         
32589         
32590     },
32591     
32592     getContainerWidth : function()
32593     {
32594        /* // container is parent if fit width
32595         var container = this.isFitWidth ? this.element.parentNode : this.element;
32596         // check that this.size and size are there
32597         // IE8 triggers resize on body size change, so they might not be
32598         
32599         var size = getSize( container );  //FIXME
32600         this.containerWidth = size && size.innerWidth; //FIXME
32601         */
32602          
32603         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32604         
32605     },
32606     
32607     _getItemLayoutPosition : function( item )  // what is item?
32608     {
32609         // we resize the item to our columnWidth..
32610       
32611         item.setWidth(this.columnWidth);
32612         item.autoBoxAdjust  = false;
32613         
32614         var sz = item.getSize();
32615  
32616         // how many columns does this brick span
32617         var remainder = this.containerWidth % this.columnWidth;
32618         
32619         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32620         // round if off by 1 pixel, otherwise use ceil
32621         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32622         colSpan = Math.min( colSpan, this.cols );
32623         
32624         // normally this should be '1' as we dont' currently allow multi width columns..
32625         
32626         var colGroup = this._getColGroup( colSpan );
32627         // get the minimum Y value from the columns
32628         var minimumY = Math.min.apply( Math, colGroup );
32629         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32630         
32631         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32632          
32633         // position the brick
32634         var position = {
32635             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32636             y: this.currentSize.y + minimumY + this.padHeight
32637         };
32638         
32639         Roo.log(position);
32640         // apply setHeight to necessary columns
32641         var setHeight = minimumY + sz.height + this.padHeight;
32642         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32643         
32644         var setSpan = this.cols + 1 - colGroup.length;
32645         for ( var i = 0; i < setSpan; i++ ) {
32646           this.colYs[ shortColIndex + i ] = setHeight ;
32647         }
32648       
32649         return position;
32650     },
32651     
32652     /**
32653      * @param {Number} colSpan - number of columns the element spans
32654      * @returns {Array} colGroup
32655      */
32656     _getColGroup : function( colSpan )
32657     {
32658         if ( colSpan < 2 ) {
32659           // if brick spans only one column, use all the column Ys
32660           return this.colYs;
32661         }
32662       
32663         var colGroup = [];
32664         // how many different places could this brick fit horizontally
32665         var groupCount = this.cols + 1 - colSpan;
32666         // for each group potential horizontal position
32667         for ( var i = 0; i < groupCount; i++ ) {
32668           // make an array of colY values for that one group
32669           var groupColYs = this.colYs.slice( i, i + colSpan );
32670           // and get the max value of the array
32671           colGroup[i] = Math.max.apply( Math, groupColYs );
32672         }
32673         return colGroup;
32674     },
32675     /*
32676     _manageStamp : function( stamp )
32677     {
32678         var stampSize =  stamp.getSize();
32679         var offset = stamp.getBox();
32680         // get the columns that this stamp affects
32681         var firstX = this.isOriginLeft ? offset.x : offset.right;
32682         var lastX = firstX + stampSize.width;
32683         var firstCol = Math.floor( firstX / this.columnWidth );
32684         firstCol = Math.max( 0, firstCol );
32685         
32686         var lastCol = Math.floor( lastX / this.columnWidth );
32687         // lastCol should not go over if multiple of columnWidth #425
32688         lastCol -= lastX % this.columnWidth ? 0 : 1;
32689         lastCol = Math.min( this.cols - 1, lastCol );
32690         
32691         // set colYs to bottom of the stamp
32692         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32693             stampSize.height;
32694             
32695         for ( var i = firstCol; i <= lastCol; i++ ) {
32696           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32697         }
32698     },
32699     */
32700     
32701     _getContainerSize : function()
32702     {
32703         this.maxY = Math.max.apply( Math, this.colYs );
32704         var size = {
32705             height: this.maxY
32706         };
32707       
32708         if ( this.isFitWidth ) {
32709             size.width = this._getContainerFitWidth();
32710         }
32711       
32712         return size;
32713     },
32714     
32715     _getContainerFitWidth : function()
32716     {
32717         var unusedCols = 0;
32718         // count unused columns
32719         var i = this.cols;
32720         while ( --i ) {
32721           if ( this.colYs[i] !== 0 ) {
32722             break;
32723           }
32724           unusedCols++;
32725         }
32726         // fit container to columns that have been used
32727         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32728     },
32729     
32730     needsResizeLayout : function()
32731     {
32732         var previousWidth = this.containerWidth;
32733         this.getContainerWidth();
32734         return previousWidth !== this.containerWidth;
32735     }
32736  
32737 });
32738
32739  
32740
32741  /*
32742  * - LGPL
32743  *
32744  * element
32745  * 
32746  */
32747
32748 /**
32749  * @class Roo.bootstrap.MasonryBrick
32750  * @extends Roo.bootstrap.Component
32751  * Bootstrap MasonryBrick class
32752  * 
32753  * @constructor
32754  * Create a new MasonryBrick
32755  * @param {Object} config The config object
32756  */
32757
32758 Roo.bootstrap.MasonryBrick = function(config){
32759     
32760     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32761     
32762     Roo.bootstrap.MasonryBrick.register(this);
32763     
32764     this.addEvents({
32765         // raw events
32766         /**
32767          * @event click
32768          * When a MasonryBrick is clcik
32769          * @param {Roo.bootstrap.MasonryBrick} this
32770          * @param {Roo.EventObject} e
32771          */
32772         "click" : true
32773     });
32774 };
32775
32776 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32777     
32778     /**
32779      * @cfg {String} title
32780      */   
32781     title : '',
32782     /**
32783      * @cfg {String} html
32784      */   
32785     html : '',
32786     /**
32787      * @cfg {String} bgimage
32788      */   
32789     bgimage : '',
32790     /**
32791      * @cfg {String} videourl
32792      */   
32793     videourl : '',
32794     /**
32795      * @cfg {String} cls
32796      */   
32797     cls : '',
32798     /**
32799      * @cfg {String} href
32800      */   
32801     href : '',
32802     /**
32803      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32804      */   
32805     size : 'xs',
32806     
32807     /**
32808      * @cfg {String} placetitle (center|bottom)
32809      */   
32810     placetitle : '',
32811     
32812     /**
32813      * @cfg {Boolean} isFitContainer defalut true
32814      */   
32815     isFitContainer : true, 
32816     
32817     /**
32818      * @cfg {Boolean} preventDefault defalut false
32819      */   
32820     preventDefault : false, 
32821     
32822     /**
32823      * @cfg {Boolean} inverse defalut false
32824      */   
32825     maskInverse : false, 
32826     
32827     getAutoCreate : function()
32828     {
32829         if(!this.isFitContainer){
32830             return this.getSplitAutoCreate();
32831         }
32832         
32833         var cls = 'masonry-brick masonry-brick-full';
32834         
32835         if(this.href.length){
32836             cls += ' masonry-brick-link';
32837         }
32838         
32839         if(this.bgimage.length){
32840             cls += ' masonry-brick-image';
32841         }
32842         
32843         if(this.maskInverse){
32844             cls += ' mask-inverse';
32845         }
32846         
32847         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32848             cls += ' enable-mask';
32849         }
32850         
32851         if(this.size){
32852             cls += ' masonry-' + this.size + '-brick';
32853         }
32854         
32855         if(this.placetitle.length){
32856             
32857             switch (this.placetitle) {
32858                 case 'center' :
32859                     cls += ' masonry-center-title';
32860                     break;
32861                 case 'bottom' :
32862                     cls += ' masonry-bottom-title';
32863                     break;
32864                 default:
32865                     break;
32866             }
32867             
32868         } else {
32869             if(!this.html.length && !this.bgimage.length){
32870                 cls += ' masonry-center-title';
32871             }
32872
32873             if(!this.html.length && this.bgimage.length){
32874                 cls += ' masonry-bottom-title';
32875             }
32876         }
32877         
32878         if(this.cls){
32879             cls += ' ' + this.cls;
32880         }
32881         
32882         var cfg = {
32883             tag: (this.href.length) ? 'a' : 'div',
32884             cls: cls,
32885             cn: [
32886                 {
32887                     tag: 'div',
32888                     cls: 'masonry-brick-mask'
32889                 },
32890                 {
32891                     tag: 'div',
32892                     cls: 'masonry-brick-paragraph',
32893                     cn: []
32894                 }
32895             ]
32896         };
32897         
32898         if(this.href.length){
32899             cfg.href = this.href;
32900         }
32901         
32902         var cn = cfg.cn[1].cn;
32903         
32904         if(this.title.length){
32905             cn.push({
32906                 tag: 'h4',
32907                 cls: 'masonry-brick-title',
32908                 html: this.title
32909             });
32910         }
32911         
32912         if(this.html.length){
32913             cn.push({
32914                 tag: 'p',
32915                 cls: 'masonry-brick-text',
32916                 html: this.html
32917             });
32918         }
32919         
32920         if (!this.title.length && !this.html.length) {
32921             cfg.cn[1].cls += ' hide';
32922         }
32923         
32924         if(this.bgimage.length){
32925             cfg.cn.push({
32926                 tag: 'img',
32927                 cls: 'masonry-brick-image-view',
32928                 src: this.bgimage
32929             });
32930         }
32931         
32932         if(this.videourl.length){
32933             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32934             // youtube support only?
32935             cfg.cn.push({
32936                 tag: 'iframe',
32937                 cls: 'masonry-brick-image-view',
32938                 src: vurl,
32939                 frameborder : 0,
32940                 allowfullscreen : true
32941             });
32942         }
32943         
32944         return cfg;
32945         
32946     },
32947     
32948     getSplitAutoCreate : function()
32949     {
32950         var cls = 'masonry-brick masonry-brick-split';
32951         
32952         if(this.href.length){
32953             cls += ' masonry-brick-link';
32954         }
32955         
32956         if(this.bgimage.length){
32957             cls += ' masonry-brick-image';
32958         }
32959         
32960         if(this.size){
32961             cls += ' masonry-' + this.size + '-brick';
32962         }
32963         
32964         switch (this.placetitle) {
32965             case 'center' :
32966                 cls += ' masonry-center-title';
32967                 break;
32968             case 'bottom' :
32969                 cls += ' masonry-bottom-title';
32970                 break;
32971             default:
32972                 if(!this.bgimage.length){
32973                     cls += ' masonry-center-title';
32974                 }
32975
32976                 if(this.bgimage.length){
32977                     cls += ' masonry-bottom-title';
32978                 }
32979                 break;
32980         }
32981         
32982         if(this.cls){
32983             cls += ' ' + this.cls;
32984         }
32985         
32986         var cfg = {
32987             tag: (this.href.length) ? 'a' : 'div',
32988             cls: cls,
32989             cn: [
32990                 {
32991                     tag: 'div',
32992                     cls: 'masonry-brick-split-head',
32993                     cn: [
32994                         {
32995                             tag: 'div',
32996                             cls: 'masonry-brick-paragraph',
32997                             cn: []
32998                         }
32999                     ]
33000                 },
33001                 {
33002                     tag: 'div',
33003                     cls: 'masonry-brick-split-body',
33004                     cn: []
33005                 }
33006             ]
33007         };
33008         
33009         if(this.href.length){
33010             cfg.href = this.href;
33011         }
33012         
33013         if(this.title.length){
33014             cfg.cn[0].cn[0].cn.push({
33015                 tag: 'h4',
33016                 cls: 'masonry-brick-title',
33017                 html: this.title
33018             });
33019         }
33020         
33021         if(this.html.length){
33022             cfg.cn[1].cn.push({
33023                 tag: 'p',
33024                 cls: 'masonry-brick-text',
33025                 html: this.html
33026             });
33027         }
33028
33029         if(this.bgimage.length){
33030             cfg.cn[0].cn.push({
33031                 tag: 'img',
33032                 cls: 'masonry-brick-image-view',
33033                 src: this.bgimage
33034             });
33035         }
33036         
33037         if(this.videourl.length){
33038             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33039             // youtube support only?
33040             cfg.cn[0].cn.cn.push({
33041                 tag: 'iframe',
33042                 cls: 'masonry-brick-image-view',
33043                 src: vurl,
33044                 frameborder : 0,
33045                 allowfullscreen : true
33046             });
33047         }
33048         
33049         return cfg;
33050     },
33051     
33052     initEvents: function() 
33053     {
33054         switch (this.size) {
33055             case 'xs' :
33056                 this.x = 1;
33057                 this.y = 1;
33058                 break;
33059             case 'sm' :
33060                 this.x = 2;
33061                 this.y = 2;
33062                 break;
33063             case 'md' :
33064             case 'md-left' :
33065             case 'md-right' :
33066                 this.x = 3;
33067                 this.y = 3;
33068                 break;
33069             case 'tall' :
33070                 this.x = 2;
33071                 this.y = 3;
33072                 break;
33073             case 'wide' :
33074                 this.x = 3;
33075                 this.y = 2;
33076                 break;
33077             case 'wide-thin' :
33078                 this.x = 3;
33079                 this.y = 1;
33080                 break;
33081                         
33082             default :
33083                 break;
33084         }
33085         
33086         if(Roo.isTouch){
33087             this.el.on('touchstart', this.onTouchStart, this);
33088             this.el.on('touchmove', this.onTouchMove, this);
33089             this.el.on('touchend', this.onTouchEnd, this);
33090             this.el.on('contextmenu', this.onContextMenu, this);
33091         } else {
33092             this.el.on('mouseenter'  ,this.enter, this);
33093             this.el.on('mouseleave', this.leave, this);
33094             this.el.on('click', this.onClick, this);
33095         }
33096         
33097         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33098             this.parent().bricks.push(this);   
33099         }
33100         
33101     },
33102     
33103     onClick: function(e, el)
33104     {
33105         var time = this.endTimer - this.startTimer;
33106         // Roo.log(e.preventDefault());
33107         if(Roo.isTouch){
33108             if(time > 1000){
33109                 e.preventDefault();
33110                 return;
33111             }
33112         }
33113         
33114         if(!this.preventDefault){
33115             return;
33116         }
33117         
33118         e.preventDefault();
33119         
33120         if (this.activeClass != '') {
33121             this.selectBrick();
33122         }
33123         
33124         this.fireEvent('click', this, e);
33125     },
33126     
33127     enter: function(e, el)
33128     {
33129         e.preventDefault();
33130         
33131         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33132             return;
33133         }
33134         
33135         if(this.bgimage.length && this.html.length){
33136             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33137         }
33138     },
33139     
33140     leave: function(e, el)
33141     {
33142         e.preventDefault();
33143         
33144         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33145             return;
33146         }
33147         
33148         if(this.bgimage.length && this.html.length){
33149             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33150         }
33151     },
33152     
33153     onTouchStart: function(e, el)
33154     {
33155 //        e.preventDefault();
33156         
33157         this.touchmoved = false;
33158         
33159         if(!this.isFitContainer){
33160             return;
33161         }
33162         
33163         if(!this.bgimage.length || !this.html.length){
33164             return;
33165         }
33166         
33167         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33168         
33169         this.timer = new Date().getTime();
33170         
33171     },
33172     
33173     onTouchMove: function(e, el)
33174     {
33175         this.touchmoved = true;
33176     },
33177     
33178     onContextMenu : function(e,el)
33179     {
33180         e.preventDefault();
33181         e.stopPropagation();
33182         return false;
33183     },
33184     
33185     onTouchEnd: function(e, el)
33186     {
33187 //        e.preventDefault();
33188         
33189         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33190         
33191             this.leave(e,el);
33192             
33193             return;
33194         }
33195         
33196         if(!this.bgimage.length || !this.html.length){
33197             
33198             if(this.href.length){
33199                 window.location.href = this.href;
33200             }
33201             
33202             return;
33203         }
33204         
33205         if(!this.isFitContainer){
33206             return;
33207         }
33208         
33209         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33210         
33211         window.location.href = this.href;
33212     },
33213     
33214     //selection on single brick only
33215     selectBrick : function() {
33216         
33217         if (!this.parentId) {
33218             return;
33219         }
33220         
33221         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33222         var index = m.selectedBrick.indexOf(this.id);
33223         
33224         if ( index > -1) {
33225             m.selectedBrick.splice(index,1);
33226             this.el.removeClass(this.activeClass);
33227             return;
33228         }
33229         
33230         for(var i = 0; i < m.selectedBrick.length; i++) {
33231             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33232             b.el.removeClass(b.activeClass);
33233         }
33234         
33235         m.selectedBrick = [];
33236         
33237         m.selectedBrick.push(this.id);
33238         this.el.addClass(this.activeClass);
33239         return;
33240     },
33241     
33242     isSelected : function(){
33243         return this.el.hasClass(this.activeClass);
33244         
33245     }
33246 });
33247
33248 Roo.apply(Roo.bootstrap.MasonryBrick, {
33249     
33250     //groups: {},
33251     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33252      /**
33253     * register a Masonry Brick
33254     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33255     */
33256     
33257     register : function(brick)
33258     {
33259         //this.groups[brick.id] = brick;
33260         this.groups.add(brick.id, brick);
33261     },
33262     /**
33263     * fetch a  masonry brick based on the masonry brick ID
33264     * @param {string} the masonry brick to add
33265     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33266     */
33267     
33268     get: function(brick_id) 
33269     {
33270         // if (typeof(this.groups[brick_id]) == 'undefined') {
33271         //     return false;
33272         // }
33273         // return this.groups[brick_id] ;
33274         
33275         if(this.groups.key(brick_id)) {
33276             return this.groups.key(brick_id);
33277         }
33278         
33279         return false;
33280     }
33281     
33282     
33283     
33284 });
33285
33286  /*
33287  * - LGPL
33288  *
33289  * element
33290  * 
33291  */
33292
33293 /**
33294  * @class Roo.bootstrap.Brick
33295  * @extends Roo.bootstrap.Component
33296  * Bootstrap Brick class
33297  * 
33298  * @constructor
33299  * Create a new Brick
33300  * @param {Object} config The config object
33301  */
33302
33303 Roo.bootstrap.Brick = function(config){
33304     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33305     
33306     this.addEvents({
33307         // raw events
33308         /**
33309          * @event click
33310          * When a Brick is click
33311          * @param {Roo.bootstrap.Brick} this
33312          * @param {Roo.EventObject} e
33313          */
33314         "click" : true
33315     });
33316 };
33317
33318 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33319     
33320     /**
33321      * @cfg {String} title
33322      */   
33323     title : '',
33324     /**
33325      * @cfg {String} html
33326      */   
33327     html : '',
33328     /**
33329      * @cfg {String} bgimage
33330      */   
33331     bgimage : '',
33332     /**
33333      * @cfg {String} cls
33334      */   
33335     cls : '',
33336     /**
33337      * @cfg {String} href
33338      */   
33339     href : '',
33340     /**
33341      * @cfg {String} video
33342      */   
33343     video : '',
33344     /**
33345      * @cfg {Boolean} square
33346      */   
33347     square : true,
33348     
33349     getAutoCreate : function()
33350     {
33351         var cls = 'roo-brick';
33352         
33353         if(this.href.length){
33354             cls += ' roo-brick-link';
33355         }
33356         
33357         if(this.bgimage.length){
33358             cls += ' roo-brick-image';
33359         }
33360         
33361         if(!this.html.length && !this.bgimage.length){
33362             cls += ' roo-brick-center-title';
33363         }
33364         
33365         if(!this.html.length && this.bgimage.length){
33366             cls += ' roo-brick-bottom-title';
33367         }
33368         
33369         if(this.cls){
33370             cls += ' ' + this.cls;
33371         }
33372         
33373         var cfg = {
33374             tag: (this.href.length) ? 'a' : 'div',
33375             cls: cls,
33376             cn: [
33377                 {
33378                     tag: 'div',
33379                     cls: 'roo-brick-paragraph',
33380                     cn: []
33381                 }
33382             ]
33383         };
33384         
33385         if(this.href.length){
33386             cfg.href = this.href;
33387         }
33388         
33389         var cn = cfg.cn[0].cn;
33390         
33391         if(this.title.length){
33392             cn.push({
33393                 tag: 'h4',
33394                 cls: 'roo-brick-title',
33395                 html: this.title
33396             });
33397         }
33398         
33399         if(this.html.length){
33400             cn.push({
33401                 tag: 'p',
33402                 cls: 'roo-brick-text',
33403                 html: this.html
33404             });
33405         } else {
33406             cn.cls += ' hide';
33407         }
33408         
33409         if(this.bgimage.length){
33410             cfg.cn.push({
33411                 tag: 'img',
33412                 cls: 'roo-brick-image-view',
33413                 src: this.bgimage
33414             });
33415         }
33416         
33417         return cfg;
33418     },
33419     
33420     initEvents: function() 
33421     {
33422         if(this.title.length || this.html.length){
33423             this.el.on('mouseenter'  ,this.enter, this);
33424             this.el.on('mouseleave', this.leave, this);
33425         }
33426         
33427         Roo.EventManager.onWindowResize(this.resize, this); 
33428         
33429         if(this.bgimage.length){
33430             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33431             this.imageEl.on('load', this.onImageLoad, this);
33432             return;
33433         }
33434         
33435         this.resize();
33436     },
33437     
33438     onImageLoad : function()
33439     {
33440         this.resize();
33441     },
33442     
33443     resize : function()
33444     {
33445         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33446         
33447         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33448         
33449         if(this.bgimage.length){
33450             var image = this.el.select('.roo-brick-image-view', true).first();
33451             
33452             image.setWidth(paragraph.getWidth());
33453             
33454             if(this.square){
33455                 image.setHeight(paragraph.getWidth());
33456             }
33457             
33458             this.el.setHeight(image.getHeight());
33459             paragraph.setHeight(image.getHeight());
33460             
33461         }
33462         
33463     },
33464     
33465     enter: function(e, el)
33466     {
33467         e.preventDefault();
33468         
33469         if(this.bgimage.length){
33470             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33471             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33472         }
33473     },
33474     
33475     leave: function(e, el)
33476     {
33477         e.preventDefault();
33478         
33479         if(this.bgimage.length){
33480             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33481             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33482         }
33483     }
33484     
33485 });
33486
33487  
33488
33489  /*
33490  * - LGPL
33491  *
33492  * Number field 
33493  */
33494
33495 /**
33496  * @class Roo.bootstrap.NumberField
33497  * @extends Roo.bootstrap.Input
33498  * Bootstrap NumberField class
33499  * 
33500  * 
33501  * 
33502  * 
33503  * @constructor
33504  * Create a new NumberField
33505  * @param {Object} config The config object
33506  */
33507
33508 Roo.bootstrap.NumberField = function(config){
33509     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33510 };
33511
33512 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33513     
33514     /**
33515      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33516      */
33517     allowDecimals : true,
33518     /**
33519      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33520      */
33521     decimalSeparator : ".",
33522     /**
33523      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33524      */
33525     decimalPrecision : 2,
33526     /**
33527      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33528      */
33529     allowNegative : true,
33530     
33531     /**
33532      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33533      */
33534     allowZero: true,
33535     /**
33536      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33537      */
33538     minValue : Number.NEGATIVE_INFINITY,
33539     /**
33540      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33541      */
33542     maxValue : Number.MAX_VALUE,
33543     /**
33544      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33545      */
33546     minText : "The minimum value for this field is {0}",
33547     /**
33548      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33549      */
33550     maxText : "The maximum value for this field is {0}",
33551     /**
33552      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33553      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33554      */
33555     nanText : "{0} is not a valid number",
33556     /**
33557      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33558      */
33559     thousandsDelimiter : false,
33560     /**
33561      * @cfg {String} valueAlign alignment of value
33562      */
33563     valueAlign : "left",
33564
33565     getAutoCreate : function()
33566     {
33567         var hiddenInput = {
33568             tag: 'input',
33569             type: 'hidden',
33570             id: Roo.id(),
33571             cls: 'hidden-number-input'
33572         };
33573         
33574         if (this.name) {
33575             hiddenInput.name = this.name;
33576         }
33577         
33578         this.name = '';
33579         
33580         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33581         
33582         this.name = hiddenInput.name;
33583         
33584         if(cfg.cn.length > 0) {
33585             cfg.cn.push(hiddenInput);
33586         }
33587         
33588         return cfg;
33589     },
33590
33591     // private
33592     initEvents : function()
33593     {   
33594         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33595         
33596         var allowed = "0123456789";
33597         
33598         if(this.allowDecimals){
33599             allowed += this.decimalSeparator;
33600         }
33601         
33602         if(this.allowNegative){
33603             allowed += "-";
33604         }
33605         
33606         if(this.thousandsDelimiter) {
33607             allowed += ",";
33608         }
33609         
33610         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33611         
33612         var keyPress = function(e){
33613             
33614             var k = e.getKey();
33615             
33616             var c = e.getCharCode();
33617             
33618             if(
33619                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33620                     allowed.indexOf(String.fromCharCode(c)) === -1
33621             ){
33622                 e.stopEvent();
33623                 return;
33624             }
33625             
33626             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33627                 return;
33628             }
33629             
33630             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33631                 e.stopEvent();
33632             }
33633         };
33634         
33635         this.el.on("keypress", keyPress, this);
33636     },
33637     
33638     validateValue : function(value)
33639     {
33640         
33641         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33642             return false;
33643         }
33644         
33645         var num = this.parseValue(value);
33646         
33647         if(isNaN(num)){
33648             this.markInvalid(String.format(this.nanText, value));
33649             return false;
33650         }
33651         
33652         if(num < this.minValue){
33653             this.markInvalid(String.format(this.minText, this.minValue));
33654             return false;
33655         }
33656         
33657         if(num > this.maxValue){
33658             this.markInvalid(String.format(this.maxText, this.maxValue));
33659             return false;
33660         }
33661         
33662         return true;
33663     },
33664
33665     getValue : function()
33666     {
33667         var v = this.hiddenEl().getValue();
33668         
33669         return this.fixPrecision(this.parseValue(v));
33670     },
33671
33672     parseValue : function(value)
33673     {
33674         if(this.thousandsDelimiter) {
33675             value += "";
33676             r = new RegExp(",", "g");
33677             value = value.replace(r, "");
33678         }
33679         
33680         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33681         return isNaN(value) ? '' : value;
33682     },
33683
33684     fixPrecision : function(value)
33685     {
33686         if(this.thousandsDelimiter) {
33687             value += "";
33688             r = new RegExp(",", "g");
33689             value = value.replace(r, "");
33690         }
33691         
33692         var nan = isNaN(value);
33693         
33694         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33695             return nan ? '' : value;
33696         }
33697         return parseFloat(value).toFixed(this.decimalPrecision);
33698     },
33699
33700     setValue : function(v)
33701     {
33702         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33703         
33704         this.value = v;
33705         
33706         if(this.rendered){
33707             
33708             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33709             
33710             this.inputEl().dom.value = (v == '') ? '' :
33711                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33712             
33713             if(!this.allowZero && v === '0') {
33714                 this.hiddenEl().dom.value = '';
33715                 this.inputEl().dom.value = '';
33716             }
33717             
33718             this.validate();
33719         }
33720     },
33721
33722     decimalPrecisionFcn : function(v)
33723     {
33724         return Math.floor(v);
33725     },
33726
33727     beforeBlur : function()
33728     {
33729         var v = this.parseValue(this.getRawValue());
33730         
33731         if(v || v === 0 || v === ''){
33732             this.setValue(v);
33733         }
33734     },
33735     
33736     hiddenEl : function()
33737     {
33738         return this.el.select('input.hidden-number-input',true).first();
33739     }
33740     
33741 });
33742
33743  
33744
33745 /*
33746 * Licence: LGPL
33747 */
33748
33749 /**
33750  * @class Roo.bootstrap.DocumentSlider
33751  * @extends Roo.bootstrap.Component
33752  * Bootstrap DocumentSlider class
33753  * 
33754  * @constructor
33755  * Create a new DocumentViewer
33756  * @param {Object} config The config object
33757  */
33758
33759 Roo.bootstrap.DocumentSlider = function(config){
33760     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33761     
33762     this.files = [];
33763     
33764     this.addEvents({
33765         /**
33766          * @event initial
33767          * Fire after initEvent
33768          * @param {Roo.bootstrap.DocumentSlider} this
33769          */
33770         "initial" : true,
33771         /**
33772          * @event update
33773          * Fire after update
33774          * @param {Roo.bootstrap.DocumentSlider} this
33775          */
33776         "update" : true,
33777         /**
33778          * @event click
33779          * Fire after click
33780          * @param {Roo.bootstrap.DocumentSlider} this
33781          */
33782         "click" : true
33783     });
33784 };
33785
33786 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33787     
33788     files : false,
33789     
33790     indicator : 0,
33791     
33792     getAutoCreate : function()
33793     {
33794         var cfg = {
33795             tag : 'div',
33796             cls : 'roo-document-slider',
33797             cn : [
33798                 {
33799                     tag : 'div',
33800                     cls : 'roo-document-slider-header',
33801                     cn : [
33802                         {
33803                             tag : 'div',
33804                             cls : 'roo-document-slider-header-title'
33805                         }
33806                     ]
33807                 },
33808                 {
33809                     tag : 'div',
33810                     cls : 'roo-document-slider-body',
33811                     cn : [
33812                         {
33813                             tag : 'div',
33814                             cls : 'roo-document-slider-prev',
33815                             cn : [
33816                                 {
33817                                     tag : 'i',
33818                                     cls : 'fa fa-chevron-left'
33819                                 }
33820                             ]
33821                         },
33822                         {
33823                             tag : 'div',
33824                             cls : 'roo-document-slider-thumb',
33825                             cn : [
33826                                 {
33827                                     tag : 'img',
33828                                     cls : 'roo-document-slider-image'
33829                                 }
33830                             ]
33831                         },
33832                         {
33833                             tag : 'div',
33834                             cls : 'roo-document-slider-next',
33835                             cn : [
33836                                 {
33837                                     tag : 'i',
33838                                     cls : 'fa fa-chevron-right'
33839                                 }
33840                             ]
33841                         }
33842                     ]
33843                 }
33844             ]
33845         };
33846         
33847         return cfg;
33848     },
33849     
33850     initEvents : function()
33851     {
33852         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33853         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33854         
33855         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33856         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33857         
33858         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33859         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33860         
33861         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33862         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33863         
33864         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33865         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33866         
33867         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33868         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33869         
33870         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33871         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33872         
33873         this.thumbEl.on('click', this.onClick, this);
33874         
33875         this.prevIndicator.on('click', this.prev, this);
33876         
33877         this.nextIndicator.on('click', this.next, this);
33878         
33879     },
33880     
33881     initial : function()
33882     {
33883         if(this.files.length){
33884             this.indicator = 1;
33885             this.update()
33886         }
33887         
33888         this.fireEvent('initial', this);
33889     },
33890     
33891     update : function()
33892     {
33893         this.imageEl.attr('src', this.files[this.indicator - 1]);
33894         
33895         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33896         
33897         this.prevIndicator.show();
33898         
33899         if(this.indicator == 1){
33900             this.prevIndicator.hide();
33901         }
33902         
33903         this.nextIndicator.show();
33904         
33905         if(this.indicator == this.files.length){
33906             this.nextIndicator.hide();
33907         }
33908         
33909         this.thumbEl.scrollTo('top');
33910         
33911         this.fireEvent('update', this);
33912     },
33913     
33914     onClick : function(e)
33915     {
33916         e.preventDefault();
33917         
33918         this.fireEvent('click', this);
33919     },
33920     
33921     prev : function(e)
33922     {
33923         e.preventDefault();
33924         
33925         this.indicator = Math.max(1, this.indicator - 1);
33926         
33927         this.update();
33928     },
33929     
33930     next : function(e)
33931     {
33932         e.preventDefault();
33933         
33934         this.indicator = Math.min(this.files.length, this.indicator + 1);
33935         
33936         this.update();
33937     }
33938 });
33939 /*
33940  * - LGPL
33941  *
33942  * RadioSet
33943  *
33944  *
33945  */
33946
33947 /**
33948  * @class Roo.bootstrap.RadioSet
33949  * @extends Roo.bootstrap.Input
33950  * Bootstrap RadioSet class
33951  * @cfg {String} indicatorpos (left|right) default left
33952  * @cfg {Boolean} inline (true|false) inline the element (default true)
33953  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33954  * @constructor
33955  * Create a new RadioSet
33956  * @param {Object} config The config object
33957  */
33958
33959 Roo.bootstrap.RadioSet = function(config){
33960     
33961     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33962     
33963     this.radioes = [];
33964     
33965     Roo.bootstrap.RadioSet.register(this);
33966     
33967     this.addEvents({
33968         /**
33969         * @event check
33970         * Fires when the element is checked or unchecked.
33971         * @param {Roo.bootstrap.RadioSet} this This radio
33972         * @param {Roo.bootstrap.Radio} item The checked item
33973         */
33974        check : true,
33975        /**
33976         * @event click
33977         * Fires when the element is click.
33978         * @param {Roo.bootstrap.RadioSet} this This radio set
33979         * @param {Roo.bootstrap.Radio} item The checked item
33980         * @param {Roo.EventObject} e The event object
33981         */
33982        click : true
33983     });
33984     
33985 };
33986
33987 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33988
33989     radioes : false,
33990     
33991     inline : true,
33992     
33993     weight : '',
33994     
33995     indicatorpos : 'left',
33996     
33997     getAutoCreate : function()
33998     {
33999         var label = {
34000             tag : 'label',
34001             cls : 'roo-radio-set-label',
34002             cn : [
34003                 {
34004                     tag : 'span',
34005                     html : this.fieldLabel
34006                 }
34007             ]
34008         };
34009         
34010         if(this.indicatorpos == 'left'){
34011             label.cn.unshift({
34012                 tag : 'i',
34013                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34014                 tooltip : 'This field is required'
34015             });
34016         } else {
34017             label.cn.push({
34018                 tag : 'i',
34019                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34020                 tooltip : 'This field is required'
34021             });
34022         }
34023         
34024         var items = {
34025             tag : 'div',
34026             cls : 'roo-radio-set-items'
34027         };
34028         
34029         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34030         
34031         if (align === 'left' && this.fieldLabel.length) {
34032             
34033             items = {
34034                 cls : "roo-radio-set-right", 
34035                 cn: [
34036                     items
34037                 ]
34038             };
34039             
34040             if(this.labelWidth > 12){
34041                 label.style = "width: " + this.labelWidth + 'px';
34042             }
34043             
34044             if(this.labelWidth < 13 && this.labelmd == 0){
34045                 this.labelmd = this.labelWidth;
34046             }
34047             
34048             if(this.labellg > 0){
34049                 label.cls += ' col-lg-' + this.labellg;
34050                 items.cls += ' col-lg-' + (12 - this.labellg);
34051             }
34052             
34053             if(this.labelmd > 0){
34054                 label.cls += ' col-md-' + this.labelmd;
34055                 items.cls += ' col-md-' + (12 - this.labelmd);
34056             }
34057             
34058             if(this.labelsm > 0){
34059                 label.cls += ' col-sm-' + this.labelsm;
34060                 items.cls += ' col-sm-' + (12 - this.labelsm);
34061             }
34062             
34063             if(this.labelxs > 0){
34064                 label.cls += ' col-xs-' + this.labelxs;
34065                 items.cls += ' col-xs-' + (12 - this.labelxs);
34066             }
34067         }
34068         
34069         var cfg = {
34070             tag : 'div',
34071             cls : 'roo-radio-set',
34072             cn : [
34073                 {
34074                     tag : 'input',
34075                     cls : 'roo-radio-set-input',
34076                     type : 'hidden',
34077                     name : this.name,
34078                     value : this.value ? this.value :  ''
34079                 },
34080                 label,
34081                 items
34082             ]
34083         };
34084         
34085         if(this.weight.length){
34086             cfg.cls += ' roo-radio-' + this.weight;
34087         }
34088         
34089         if(this.inline) {
34090             cfg.cls += ' roo-radio-set-inline';
34091         }
34092         
34093         var settings=this;
34094         ['xs','sm','md','lg'].map(function(size){
34095             if (settings[size]) {
34096                 cfg.cls += ' col-' + size + '-' + settings[size];
34097             }
34098         });
34099         
34100         return cfg;
34101         
34102     },
34103
34104     initEvents : function()
34105     {
34106         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34107         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34108         
34109         if(!this.fieldLabel.length){
34110             this.labelEl.hide();
34111         }
34112         
34113         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34114         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34115         
34116         this.indicator = this.indicatorEl();
34117         
34118         if(this.indicator){
34119             this.indicator.addClass('invisible');
34120         }
34121         
34122         this.originalValue = this.getValue();
34123         
34124     },
34125     
34126     inputEl: function ()
34127     {
34128         return this.el.select('.roo-radio-set-input', true).first();
34129     },
34130     
34131     getChildContainer : function()
34132     {
34133         return this.itemsEl;
34134     },
34135     
34136     register : function(item)
34137     {
34138         this.radioes.push(item);
34139         
34140     },
34141     
34142     validate : function()
34143     {   
34144         if(this.getVisibilityEl().hasClass('hidden')){
34145             return true;
34146         }
34147         
34148         var valid = false;
34149         
34150         Roo.each(this.radioes, function(i){
34151             if(!i.checked){
34152                 return;
34153             }
34154             
34155             valid = true;
34156             return false;
34157         });
34158         
34159         if(this.allowBlank) {
34160             return true;
34161         }
34162         
34163         if(this.disabled || valid){
34164             this.markValid();
34165             return true;
34166         }
34167         
34168         this.markInvalid();
34169         return false;
34170         
34171     },
34172     
34173     markValid : function()
34174     {
34175         if(this.labelEl.isVisible(true)){
34176             this.indicatorEl().removeClass('visible');
34177             this.indicatorEl().addClass('invisible');
34178         }
34179         
34180         this.el.removeClass([this.invalidClass, this.validClass]);
34181         this.el.addClass(this.validClass);
34182         
34183         this.fireEvent('valid', this);
34184     },
34185     
34186     markInvalid : function(msg)
34187     {
34188         if(this.allowBlank || this.disabled){
34189             return;
34190         }
34191         
34192         if(this.labelEl.isVisible(true)){
34193             this.indicatorEl().removeClass('invisible');
34194             this.indicatorEl().addClass('visible');
34195         }
34196         
34197         this.el.removeClass([this.invalidClass, this.validClass]);
34198         this.el.addClass(this.invalidClass);
34199         
34200         this.fireEvent('invalid', this, msg);
34201         
34202     },
34203     
34204     setValue : function(v, suppressEvent)
34205     {   
34206         if(this.value === v){
34207             return;
34208         }
34209         
34210         this.value = v;
34211         
34212         if(this.rendered){
34213             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34214         }
34215         
34216         Roo.each(this.radioes, function(i){
34217             i.checked = false;
34218             i.el.removeClass('checked');
34219         });
34220         
34221         Roo.each(this.radioes, function(i){
34222             
34223             if(i.value === v || i.value.toString() === v.toString()){
34224                 i.checked = true;
34225                 i.el.addClass('checked');
34226                 
34227                 if(suppressEvent !== true){
34228                     this.fireEvent('check', this, i);
34229                 }
34230                 
34231                 return false;
34232             }
34233             
34234         }, this);
34235         
34236         this.validate();
34237     },
34238     
34239     clearInvalid : function(){
34240         
34241         if(!this.el || this.preventMark){
34242             return;
34243         }
34244         
34245         this.el.removeClass([this.invalidClass]);
34246         
34247         this.fireEvent('valid', this);
34248     }
34249     
34250 });
34251
34252 Roo.apply(Roo.bootstrap.RadioSet, {
34253     
34254     groups: {},
34255     
34256     register : function(set)
34257     {
34258         this.groups[set.name] = set;
34259     },
34260     
34261     get: function(name) 
34262     {
34263         if (typeof(this.groups[name]) == 'undefined') {
34264             return false;
34265         }
34266         
34267         return this.groups[name] ;
34268     }
34269     
34270 });
34271 /*
34272  * Based on:
34273  * Ext JS Library 1.1.1
34274  * Copyright(c) 2006-2007, Ext JS, LLC.
34275  *
34276  * Originally Released Under LGPL - original licence link has changed is not relivant.
34277  *
34278  * Fork - LGPL
34279  * <script type="text/javascript">
34280  */
34281
34282
34283 /**
34284  * @class Roo.bootstrap.SplitBar
34285  * @extends Roo.util.Observable
34286  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34287  * <br><br>
34288  * Usage:
34289  * <pre><code>
34290 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34291                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34292 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34293 split.minSize = 100;
34294 split.maxSize = 600;
34295 split.animate = true;
34296 split.on('moved', splitterMoved);
34297 </code></pre>
34298  * @constructor
34299  * Create a new SplitBar
34300  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34301  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34302  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34303  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34304                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34305                         position of the SplitBar).
34306  */
34307 Roo.bootstrap.SplitBar = function(cfg){
34308     
34309     /** @private */
34310     
34311     //{
34312     //  dragElement : elm
34313     //  resizingElement: el,
34314         // optional..
34315     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34316     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34317         // existingProxy ???
34318     //}
34319     
34320     this.el = Roo.get(cfg.dragElement, true);
34321     this.el.dom.unselectable = "on";
34322     /** @private */
34323     this.resizingEl = Roo.get(cfg.resizingElement, true);
34324
34325     /**
34326      * @private
34327      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34328      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34329      * @type Number
34330      */
34331     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34332     
34333     /**
34334      * The minimum size of the resizing element. (Defaults to 0)
34335      * @type Number
34336      */
34337     this.minSize = 0;
34338     
34339     /**
34340      * The maximum size of the resizing element. (Defaults to 2000)
34341      * @type Number
34342      */
34343     this.maxSize = 2000;
34344     
34345     /**
34346      * Whether to animate the transition to the new size
34347      * @type Boolean
34348      */
34349     this.animate = false;
34350     
34351     /**
34352      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34353      * @type Boolean
34354      */
34355     this.useShim = false;
34356     
34357     /** @private */
34358     this.shim = null;
34359     
34360     if(!cfg.existingProxy){
34361         /** @private */
34362         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34363     }else{
34364         this.proxy = Roo.get(cfg.existingProxy).dom;
34365     }
34366     /** @private */
34367     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34368     
34369     /** @private */
34370     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34371     
34372     /** @private */
34373     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34374     
34375     /** @private */
34376     this.dragSpecs = {};
34377     
34378     /**
34379      * @private The adapter to use to positon and resize elements
34380      */
34381     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34382     this.adapter.init(this);
34383     
34384     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34385         /** @private */
34386         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34387         this.el.addClass("roo-splitbar-h");
34388     }else{
34389         /** @private */
34390         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34391         this.el.addClass("roo-splitbar-v");
34392     }
34393     
34394     this.addEvents({
34395         /**
34396          * @event resize
34397          * Fires when the splitter is moved (alias for {@link #event-moved})
34398          * @param {Roo.bootstrap.SplitBar} this
34399          * @param {Number} newSize the new width or height
34400          */
34401         "resize" : true,
34402         /**
34403          * @event moved
34404          * Fires when the splitter is moved
34405          * @param {Roo.bootstrap.SplitBar} this
34406          * @param {Number} newSize the new width or height
34407          */
34408         "moved" : true,
34409         /**
34410          * @event beforeresize
34411          * Fires before the splitter is dragged
34412          * @param {Roo.bootstrap.SplitBar} this
34413          */
34414         "beforeresize" : true,
34415
34416         "beforeapply" : true
34417     });
34418
34419     Roo.util.Observable.call(this);
34420 };
34421
34422 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34423     onStartProxyDrag : function(x, y){
34424         this.fireEvent("beforeresize", this);
34425         if(!this.overlay){
34426             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34427             o.unselectable();
34428             o.enableDisplayMode("block");
34429             // all splitbars share the same overlay
34430             Roo.bootstrap.SplitBar.prototype.overlay = o;
34431         }
34432         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34433         this.overlay.show();
34434         Roo.get(this.proxy).setDisplayed("block");
34435         var size = this.adapter.getElementSize(this);
34436         this.activeMinSize = this.getMinimumSize();;
34437         this.activeMaxSize = this.getMaximumSize();;
34438         var c1 = size - this.activeMinSize;
34439         var c2 = Math.max(this.activeMaxSize - size, 0);
34440         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34441             this.dd.resetConstraints();
34442             this.dd.setXConstraint(
34443                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34444                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34445             );
34446             this.dd.setYConstraint(0, 0);
34447         }else{
34448             this.dd.resetConstraints();
34449             this.dd.setXConstraint(0, 0);
34450             this.dd.setYConstraint(
34451                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34452                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34453             );
34454          }
34455         this.dragSpecs.startSize = size;
34456         this.dragSpecs.startPoint = [x, y];
34457         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34458     },
34459     
34460     /** 
34461      * @private Called after the drag operation by the DDProxy
34462      */
34463     onEndProxyDrag : function(e){
34464         Roo.get(this.proxy).setDisplayed(false);
34465         var endPoint = Roo.lib.Event.getXY(e);
34466         if(this.overlay){
34467             this.overlay.hide();
34468         }
34469         var newSize;
34470         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34471             newSize = this.dragSpecs.startSize + 
34472                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34473                     endPoint[0] - this.dragSpecs.startPoint[0] :
34474                     this.dragSpecs.startPoint[0] - endPoint[0]
34475                 );
34476         }else{
34477             newSize = this.dragSpecs.startSize + 
34478                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34479                     endPoint[1] - this.dragSpecs.startPoint[1] :
34480                     this.dragSpecs.startPoint[1] - endPoint[1]
34481                 );
34482         }
34483         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34484         if(newSize != this.dragSpecs.startSize){
34485             if(this.fireEvent('beforeapply', this, newSize) !== false){
34486                 this.adapter.setElementSize(this, newSize);
34487                 this.fireEvent("moved", this, newSize);
34488                 this.fireEvent("resize", this, newSize);
34489             }
34490         }
34491     },
34492     
34493     /**
34494      * Get the adapter this SplitBar uses
34495      * @return The adapter object
34496      */
34497     getAdapter : function(){
34498         return this.adapter;
34499     },
34500     
34501     /**
34502      * Set the adapter this SplitBar uses
34503      * @param {Object} adapter A SplitBar adapter object
34504      */
34505     setAdapter : function(adapter){
34506         this.adapter = adapter;
34507         this.adapter.init(this);
34508     },
34509     
34510     /**
34511      * Gets the minimum size for the resizing element
34512      * @return {Number} The minimum size
34513      */
34514     getMinimumSize : function(){
34515         return this.minSize;
34516     },
34517     
34518     /**
34519      * Sets the minimum size for the resizing element
34520      * @param {Number} minSize The minimum size
34521      */
34522     setMinimumSize : function(minSize){
34523         this.minSize = minSize;
34524     },
34525     
34526     /**
34527      * Gets the maximum size for the resizing element
34528      * @return {Number} The maximum size
34529      */
34530     getMaximumSize : function(){
34531         return this.maxSize;
34532     },
34533     
34534     /**
34535      * Sets the maximum size for the resizing element
34536      * @param {Number} maxSize The maximum size
34537      */
34538     setMaximumSize : function(maxSize){
34539         this.maxSize = maxSize;
34540     },
34541     
34542     /**
34543      * Sets the initialize size for the resizing element
34544      * @param {Number} size The initial size
34545      */
34546     setCurrentSize : function(size){
34547         var oldAnimate = this.animate;
34548         this.animate = false;
34549         this.adapter.setElementSize(this, size);
34550         this.animate = oldAnimate;
34551     },
34552     
34553     /**
34554      * Destroy this splitbar. 
34555      * @param {Boolean} removeEl True to remove the element
34556      */
34557     destroy : function(removeEl){
34558         if(this.shim){
34559             this.shim.remove();
34560         }
34561         this.dd.unreg();
34562         this.proxy.parentNode.removeChild(this.proxy);
34563         if(removeEl){
34564             this.el.remove();
34565         }
34566     }
34567 });
34568
34569 /**
34570  * @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.
34571  */
34572 Roo.bootstrap.SplitBar.createProxy = function(dir){
34573     var proxy = new Roo.Element(document.createElement("div"));
34574     proxy.unselectable();
34575     var cls = 'roo-splitbar-proxy';
34576     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34577     document.body.appendChild(proxy.dom);
34578     return proxy.dom;
34579 };
34580
34581 /** 
34582  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34583  * Default Adapter. It assumes the splitter and resizing element are not positioned
34584  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34585  */
34586 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34587 };
34588
34589 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34590     // do nothing for now
34591     init : function(s){
34592     
34593     },
34594     /**
34595      * Called before drag operations to get the current size of the resizing element. 
34596      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34597      */
34598      getElementSize : function(s){
34599         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34600             return s.resizingEl.getWidth();
34601         }else{
34602             return s.resizingEl.getHeight();
34603         }
34604     },
34605     
34606     /**
34607      * Called after drag operations to set the size of the resizing element.
34608      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34609      * @param {Number} newSize The new size to set
34610      * @param {Function} onComplete A function to be invoked when resizing is complete
34611      */
34612     setElementSize : function(s, newSize, onComplete){
34613         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34614             if(!s.animate){
34615                 s.resizingEl.setWidth(newSize);
34616                 if(onComplete){
34617                     onComplete(s, newSize);
34618                 }
34619             }else{
34620                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34621             }
34622         }else{
34623             
34624             if(!s.animate){
34625                 s.resizingEl.setHeight(newSize);
34626                 if(onComplete){
34627                     onComplete(s, newSize);
34628                 }
34629             }else{
34630                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34631             }
34632         }
34633     }
34634 };
34635
34636 /** 
34637  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34638  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34639  * Adapter that  moves the splitter element to align with the resized sizing element. 
34640  * Used with an absolute positioned SplitBar.
34641  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34642  * document.body, make sure you assign an id to the body element.
34643  */
34644 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34645     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34646     this.container = Roo.get(container);
34647 };
34648
34649 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34650     init : function(s){
34651         this.basic.init(s);
34652     },
34653     
34654     getElementSize : function(s){
34655         return this.basic.getElementSize(s);
34656     },
34657     
34658     setElementSize : function(s, newSize, onComplete){
34659         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34660     },
34661     
34662     moveSplitter : function(s){
34663         var yes = Roo.bootstrap.SplitBar;
34664         switch(s.placement){
34665             case yes.LEFT:
34666                 s.el.setX(s.resizingEl.getRight());
34667                 break;
34668             case yes.RIGHT:
34669                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34670                 break;
34671             case yes.TOP:
34672                 s.el.setY(s.resizingEl.getBottom());
34673                 break;
34674             case yes.BOTTOM:
34675                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34676                 break;
34677         }
34678     }
34679 };
34680
34681 /**
34682  * Orientation constant - Create a vertical SplitBar
34683  * @static
34684  * @type Number
34685  */
34686 Roo.bootstrap.SplitBar.VERTICAL = 1;
34687
34688 /**
34689  * Orientation constant - Create a horizontal SplitBar
34690  * @static
34691  * @type Number
34692  */
34693 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34694
34695 /**
34696  * Placement constant - The resizing element is to the left of the splitter element
34697  * @static
34698  * @type Number
34699  */
34700 Roo.bootstrap.SplitBar.LEFT = 1;
34701
34702 /**
34703  * Placement constant - The resizing element is to the right of the splitter element
34704  * @static
34705  * @type Number
34706  */
34707 Roo.bootstrap.SplitBar.RIGHT = 2;
34708
34709 /**
34710  * Placement constant - The resizing element is positioned above the splitter element
34711  * @static
34712  * @type Number
34713  */
34714 Roo.bootstrap.SplitBar.TOP = 3;
34715
34716 /**
34717  * Placement constant - The resizing element is positioned under splitter element
34718  * @static
34719  * @type Number
34720  */
34721 Roo.bootstrap.SplitBar.BOTTOM = 4;
34722 Roo.namespace("Roo.bootstrap.layout");/*
34723  * Based on:
34724  * Ext JS Library 1.1.1
34725  * Copyright(c) 2006-2007, Ext JS, LLC.
34726  *
34727  * Originally Released Under LGPL - original licence link has changed is not relivant.
34728  *
34729  * Fork - LGPL
34730  * <script type="text/javascript">
34731  */
34732
34733 /**
34734  * @class Roo.bootstrap.layout.Manager
34735  * @extends Roo.bootstrap.Component
34736  * Base class for layout managers.
34737  */
34738 Roo.bootstrap.layout.Manager = function(config)
34739 {
34740     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34741
34742
34743
34744
34745
34746     /** false to disable window resize monitoring @type Boolean */
34747     this.monitorWindowResize = true;
34748     this.regions = {};
34749     this.addEvents({
34750         /**
34751          * @event layout
34752          * Fires when a layout is performed.
34753          * @param {Roo.LayoutManager} this
34754          */
34755         "layout" : true,
34756         /**
34757          * @event regionresized
34758          * Fires when the user resizes a region.
34759          * @param {Roo.LayoutRegion} region The resized region
34760          * @param {Number} newSize The new size (width for east/west, height for north/south)
34761          */
34762         "regionresized" : true,
34763         /**
34764          * @event regioncollapsed
34765          * Fires when a region is collapsed.
34766          * @param {Roo.LayoutRegion} region The collapsed region
34767          */
34768         "regioncollapsed" : true,
34769         /**
34770          * @event regionexpanded
34771          * Fires when a region is expanded.
34772          * @param {Roo.LayoutRegion} region The expanded region
34773          */
34774         "regionexpanded" : true
34775     });
34776     this.updating = false;
34777
34778     if (config.el) {
34779         this.el = Roo.get(config.el);
34780         this.initEvents();
34781     }
34782
34783 };
34784
34785 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34786
34787
34788     regions : null,
34789
34790     monitorWindowResize : true,
34791
34792
34793     updating : false,
34794
34795
34796     onRender : function(ct, position)
34797     {
34798         if(!this.el){
34799             this.el = Roo.get(ct);
34800             this.initEvents();
34801         }
34802         //this.fireEvent('render',this);
34803     },
34804
34805
34806     initEvents: function()
34807     {
34808
34809
34810         // ie scrollbar fix
34811         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34812             document.body.scroll = "no";
34813         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34814             this.el.position('relative');
34815         }
34816         this.id = this.el.id;
34817         this.el.addClass("roo-layout-container");
34818         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34819         if(this.el.dom != document.body ) {
34820             this.el.on('resize', this.layout,this);
34821             this.el.on('show', this.layout,this);
34822         }
34823
34824     },
34825
34826     /**
34827      * Returns true if this layout is currently being updated
34828      * @return {Boolean}
34829      */
34830     isUpdating : function(){
34831         return this.updating;
34832     },
34833
34834     /**
34835      * Suspend the LayoutManager from doing auto-layouts while
34836      * making multiple add or remove calls
34837      */
34838     beginUpdate : function(){
34839         this.updating = true;
34840     },
34841
34842     /**
34843      * Restore auto-layouts and optionally disable the manager from performing a layout
34844      * @param {Boolean} noLayout true to disable a layout update
34845      */
34846     endUpdate : function(noLayout){
34847         this.updating = false;
34848         if(!noLayout){
34849             this.layout();
34850         }
34851     },
34852
34853     layout: function(){
34854         // abstract...
34855     },
34856
34857     onRegionResized : function(region, newSize){
34858         this.fireEvent("regionresized", region, newSize);
34859         this.layout();
34860     },
34861
34862     onRegionCollapsed : function(region){
34863         this.fireEvent("regioncollapsed", region);
34864     },
34865
34866     onRegionExpanded : function(region){
34867         this.fireEvent("regionexpanded", region);
34868     },
34869
34870     /**
34871      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34872      * performs box-model adjustments.
34873      * @return {Object} The size as an object {width: (the width), height: (the height)}
34874      */
34875     getViewSize : function()
34876     {
34877         var size;
34878         if(this.el.dom != document.body){
34879             size = this.el.getSize();
34880         }else{
34881             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34882         }
34883         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34884         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34885         return size;
34886     },
34887
34888     /**
34889      * Returns the Element this layout is bound to.
34890      * @return {Roo.Element}
34891      */
34892     getEl : function(){
34893         return this.el;
34894     },
34895
34896     /**
34897      * Returns the specified region.
34898      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34899      * @return {Roo.LayoutRegion}
34900      */
34901     getRegion : function(target){
34902         return this.regions[target.toLowerCase()];
34903     },
34904
34905     onWindowResize : function(){
34906         if(this.monitorWindowResize){
34907             this.layout();
34908         }
34909     }
34910 });
34911 /*
34912  * Based on:
34913  * Ext JS Library 1.1.1
34914  * Copyright(c) 2006-2007, Ext JS, LLC.
34915  *
34916  * Originally Released Under LGPL - original licence link has changed is not relivant.
34917  *
34918  * Fork - LGPL
34919  * <script type="text/javascript">
34920  */
34921 /**
34922  * @class Roo.bootstrap.layout.Border
34923  * @extends Roo.bootstrap.layout.Manager
34924  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34925  * please see: examples/bootstrap/nested.html<br><br>
34926  
34927 <b>The container the layout is rendered into can be either the body element or any other element.
34928 If it is not the body element, the container needs to either be an absolute positioned element,
34929 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34930 the container size if it is not the body element.</b>
34931
34932 * @constructor
34933 * Create a new Border
34934 * @param {Object} config Configuration options
34935  */
34936 Roo.bootstrap.layout.Border = function(config){
34937     config = config || {};
34938     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34939     
34940     
34941     
34942     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34943         if(config[region]){
34944             config[region].region = region;
34945             this.addRegion(config[region]);
34946         }
34947     },this);
34948     
34949 };
34950
34951 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34952
34953 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34954     /**
34955      * Creates and adds a new region if it doesn't already exist.
34956      * @param {String} target The target region key (north, south, east, west or center).
34957      * @param {Object} config The regions config object
34958      * @return {BorderLayoutRegion} The new region
34959      */
34960     addRegion : function(config)
34961     {
34962         if(!this.regions[config.region]){
34963             var r = this.factory(config);
34964             this.bindRegion(r);
34965         }
34966         return this.regions[config.region];
34967     },
34968
34969     // private (kinda)
34970     bindRegion : function(r){
34971         this.regions[r.config.region] = r;
34972         
34973         r.on("visibilitychange",    this.layout, this);
34974         r.on("paneladded",          this.layout, this);
34975         r.on("panelremoved",        this.layout, this);
34976         r.on("invalidated",         this.layout, this);
34977         r.on("resized",             this.onRegionResized, this);
34978         r.on("collapsed",           this.onRegionCollapsed, this);
34979         r.on("expanded",            this.onRegionExpanded, this);
34980     },
34981
34982     /**
34983      * Performs a layout update.
34984      */
34985     layout : function()
34986     {
34987         if(this.updating) {
34988             return;
34989         }
34990         
34991         // render all the rebions if they have not been done alreayd?
34992         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34993             if(this.regions[region] && !this.regions[region].bodyEl){
34994                 this.regions[region].onRender(this.el)
34995             }
34996         },this);
34997         
34998         var size = this.getViewSize();
34999         var w = size.width;
35000         var h = size.height;
35001         var centerW = w;
35002         var centerH = h;
35003         var centerY = 0;
35004         var centerX = 0;
35005         //var x = 0, y = 0;
35006
35007         var rs = this.regions;
35008         var north = rs["north"];
35009         var south = rs["south"]; 
35010         var west = rs["west"];
35011         var east = rs["east"];
35012         var center = rs["center"];
35013         //if(this.hideOnLayout){ // not supported anymore
35014             //c.el.setStyle("display", "none");
35015         //}
35016         if(north && north.isVisible()){
35017             var b = north.getBox();
35018             var m = north.getMargins();
35019             b.width = w - (m.left+m.right);
35020             b.x = m.left;
35021             b.y = m.top;
35022             centerY = b.height + b.y + m.bottom;
35023             centerH -= centerY;
35024             north.updateBox(this.safeBox(b));
35025         }
35026         if(south && south.isVisible()){
35027             var b = south.getBox();
35028             var m = south.getMargins();
35029             b.width = w - (m.left+m.right);
35030             b.x = m.left;
35031             var totalHeight = (b.height + m.top + m.bottom);
35032             b.y = h - totalHeight + m.top;
35033             centerH -= totalHeight;
35034             south.updateBox(this.safeBox(b));
35035         }
35036         if(west && west.isVisible()){
35037             var b = west.getBox();
35038             var m = west.getMargins();
35039             b.height = centerH - (m.top+m.bottom);
35040             b.x = m.left;
35041             b.y = centerY + m.top;
35042             var totalWidth = (b.width + m.left + m.right);
35043             centerX += totalWidth;
35044             centerW -= totalWidth;
35045             west.updateBox(this.safeBox(b));
35046         }
35047         if(east && east.isVisible()){
35048             var b = east.getBox();
35049             var m = east.getMargins();
35050             b.height = centerH - (m.top+m.bottom);
35051             var totalWidth = (b.width + m.left + m.right);
35052             b.x = w - totalWidth + m.left;
35053             b.y = centerY + m.top;
35054             centerW -= totalWidth;
35055             east.updateBox(this.safeBox(b));
35056         }
35057         if(center){
35058             var m = center.getMargins();
35059             var centerBox = {
35060                 x: centerX + m.left,
35061                 y: centerY + m.top,
35062                 width: centerW - (m.left+m.right),
35063                 height: centerH - (m.top+m.bottom)
35064             };
35065             //if(this.hideOnLayout){
35066                 //center.el.setStyle("display", "block");
35067             //}
35068             center.updateBox(this.safeBox(centerBox));
35069         }
35070         this.el.repaint();
35071         this.fireEvent("layout", this);
35072     },
35073
35074     // private
35075     safeBox : function(box){
35076         box.width = Math.max(0, box.width);
35077         box.height = Math.max(0, box.height);
35078         return box;
35079     },
35080
35081     /**
35082      * Adds a ContentPanel (or subclass) to this layout.
35083      * @param {String} target The target region key (north, south, east, west or center).
35084      * @param {Roo.ContentPanel} panel The panel to add
35085      * @return {Roo.ContentPanel} The added panel
35086      */
35087     add : function(target, panel){
35088          
35089         target = target.toLowerCase();
35090         return this.regions[target].add(panel);
35091     },
35092
35093     /**
35094      * Remove a ContentPanel (or subclass) to this layout.
35095      * @param {String} target The target region key (north, south, east, west or center).
35096      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35097      * @return {Roo.ContentPanel} The removed panel
35098      */
35099     remove : function(target, panel){
35100         target = target.toLowerCase();
35101         return this.regions[target].remove(panel);
35102     },
35103
35104     /**
35105      * Searches all regions for a panel with the specified id
35106      * @param {String} panelId
35107      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35108      */
35109     findPanel : function(panelId){
35110         var rs = this.regions;
35111         for(var target in rs){
35112             if(typeof rs[target] != "function"){
35113                 var p = rs[target].getPanel(panelId);
35114                 if(p){
35115                     return p;
35116                 }
35117             }
35118         }
35119         return null;
35120     },
35121
35122     /**
35123      * Searches all regions for a panel with the specified id and activates (shows) it.
35124      * @param {String/ContentPanel} panelId The panels id or the panel itself
35125      * @return {Roo.ContentPanel} The shown panel or null
35126      */
35127     showPanel : function(panelId) {
35128       var rs = this.regions;
35129       for(var target in rs){
35130          var r = rs[target];
35131          if(typeof r != "function"){
35132             if(r.hasPanel(panelId)){
35133                return r.showPanel(panelId);
35134             }
35135          }
35136       }
35137       return null;
35138    },
35139
35140    /**
35141      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35142      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35143      */
35144    /*
35145     restoreState : function(provider){
35146         if(!provider){
35147             provider = Roo.state.Manager;
35148         }
35149         var sm = new Roo.LayoutStateManager();
35150         sm.init(this, provider);
35151     },
35152 */
35153  
35154  
35155     /**
35156      * Adds a xtype elements to the layout.
35157      * <pre><code>
35158
35159 layout.addxtype({
35160        xtype : 'ContentPanel',
35161        region: 'west',
35162        items: [ .... ]
35163    }
35164 );
35165
35166 layout.addxtype({
35167         xtype : 'NestedLayoutPanel',
35168         region: 'west',
35169         layout: {
35170            center: { },
35171            west: { }   
35172         },
35173         items : [ ... list of content panels or nested layout panels.. ]
35174    }
35175 );
35176 </code></pre>
35177      * @param {Object} cfg Xtype definition of item to add.
35178      */
35179     addxtype : function(cfg)
35180     {
35181         // basically accepts a pannel...
35182         // can accept a layout region..!?!?
35183         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35184         
35185         
35186         // theory?  children can only be panels??
35187         
35188         //if (!cfg.xtype.match(/Panel$/)) {
35189         //    return false;
35190         //}
35191         var ret = false;
35192         
35193         if (typeof(cfg.region) == 'undefined') {
35194             Roo.log("Failed to add Panel, region was not set");
35195             Roo.log(cfg);
35196             return false;
35197         }
35198         var region = cfg.region;
35199         delete cfg.region;
35200         
35201           
35202         var xitems = [];
35203         if (cfg.items) {
35204             xitems = cfg.items;
35205             delete cfg.items;
35206         }
35207         var nb = false;
35208         
35209         switch(cfg.xtype) 
35210         {
35211             case 'Content':  // ContentPanel (el, cfg)
35212             case 'Scroll':  // ContentPanel (el, cfg)
35213             case 'View': 
35214                 cfg.autoCreate = true;
35215                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35216                 //} else {
35217                 //    var el = this.el.createChild();
35218                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35219                 //}
35220                 
35221                 this.add(region, ret);
35222                 break;
35223             
35224             /*
35225             case 'TreePanel': // our new panel!
35226                 cfg.el = this.el.createChild();
35227                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35228                 this.add(region, ret);
35229                 break;
35230             */
35231             
35232             case 'Nest': 
35233                 // create a new Layout (which is  a Border Layout...
35234                 
35235                 var clayout = cfg.layout;
35236                 clayout.el  = this.el.createChild();
35237                 clayout.items   = clayout.items  || [];
35238                 
35239                 delete cfg.layout;
35240                 
35241                 // replace this exitems with the clayout ones..
35242                 xitems = clayout.items;
35243                  
35244                 // force background off if it's in center...
35245                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35246                     cfg.background = false;
35247                 }
35248                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35249                 
35250                 
35251                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35252                 //console.log('adding nested layout panel '  + cfg.toSource());
35253                 this.add(region, ret);
35254                 nb = {}; /// find first...
35255                 break;
35256             
35257             case 'Grid':
35258                 
35259                 // needs grid and region
35260                 
35261                 //var el = this.getRegion(region).el.createChild();
35262                 /*
35263                  *var el = this.el.createChild();
35264                 // create the grid first...
35265                 cfg.grid.container = el;
35266                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35267                 */
35268                 
35269                 if (region == 'center' && this.active ) {
35270                     cfg.background = false;
35271                 }
35272                 
35273                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35274                 
35275                 this.add(region, ret);
35276                 /*
35277                 if (cfg.background) {
35278                     // render grid on panel activation (if panel background)
35279                     ret.on('activate', function(gp) {
35280                         if (!gp.grid.rendered) {
35281                     //        gp.grid.render(el);
35282                         }
35283                     });
35284                 } else {
35285                   //  cfg.grid.render(el);
35286                 }
35287                 */
35288                 break;
35289            
35290            
35291             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35292                 // it was the old xcomponent building that caused this before.
35293                 // espeically if border is the top element in the tree.
35294                 ret = this;
35295                 break; 
35296                 
35297                     
35298                 
35299                 
35300                 
35301             default:
35302                 /*
35303                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35304                     
35305                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35306                     this.add(region, ret);
35307                 } else {
35308                 */
35309                     Roo.log(cfg);
35310                     throw "Can not add '" + cfg.xtype + "' to Border";
35311                     return null;
35312              
35313                                 
35314              
35315         }
35316         this.beginUpdate();
35317         // add children..
35318         var region = '';
35319         var abn = {};
35320         Roo.each(xitems, function(i)  {
35321             region = nb && i.region ? i.region : false;
35322             
35323             var add = ret.addxtype(i);
35324            
35325             if (region) {
35326                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35327                 if (!i.background) {
35328                     abn[region] = nb[region] ;
35329                 }
35330             }
35331             
35332         });
35333         this.endUpdate();
35334
35335         // make the last non-background panel active..
35336         //if (nb) { Roo.log(abn); }
35337         if (nb) {
35338             
35339             for(var r in abn) {
35340                 region = this.getRegion(r);
35341                 if (region) {
35342                     // tried using nb[r], but it does not work..
35343                      
35344                     region.showPanel(abn[r]);
35345                    
35346                 }
35347             }
35348         }
35349         return ret;
35350         
35351     },
35352     
35353     
35354 // private
35355     factory : function(cfg)
35356     {
35357         
35358         var validRegions = Roo.bootstrap.layout.Border.regions;
35359
35360         var target = cfg.region;
35361         cfg.mgr = this;
35362         
35363         var r = Roo.bootstrap.layout;
35364         Roo.log(target);
35365         switch(target){
35366             case "north":
35367                 return new r.North(cfg);
35368             case "south":
35369                 return new r.South(cfg);
35370             case "east":
35371                 return new r.East(cfg);
35372             case "west":
35373                 return new r.West(cfg);
35374             case "center":
35375                 return new r.Center(cfg);
35376         }
35377         throw 'Layout region "'+target+'" not supported.';
35378     }
35379     
35380     
35381 });
35382  /*
35383  * Based on:
35384  * Ext JS Library 1.1.1
35385  * Copyright(c) 2006-2007, Ext JS, LLC.
35386  *
35387  * Originally Released Under LGPL - original licence link has changed is not relivant.
35388  *
35389  * Fork - LGPL
35390  * <script type="text/javascript">
35391  */
35392  
35393 /**
35394  * @class Roo.bootstrap.layout.Basic
35395  * @extends Roo.util.Observable
35396  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35397  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35398  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35399  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35400  * @cfg {string}   region  the region that it inhabits..
35401  * @cfg {bool}   skipConfig skip config?
35402  * 
35403
35404  */
35405 Roo.bootstrap.layout.Basic = function(config){
35406     
35407     this.mgr = config.mgr;
35408     
35409     this.position = config.region;
35410     
35411     var skipConfig = config.skipConfig;
35412     
35413     this.events = {
35414         /**
35415          * @scope Roo.BasicLayoutRegion
35416          */
35417         
35418         /**
35419          * @event beforeremove
35420          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35421          * @param {Roo.LayoutRegion} this
35422          * @param {Roo.ContentPanel} panel The panel
35423          * @param {Object} e The cancel event object
35424          */
35425         "beforeremove" : true,
35426         /**
35427          * @event invalidated
35428          * Fires when the layout for this region is changed.
35429          * @param {Roo.LayoutRegion} this
35430          */
35431         "invalidated" : true,
35432         /**
35433          * @event visibilitychange
35434          * Fires when this region is shown or hidden 
35435          * @param {Roo.LayoutRegion} this
35436          * @param {Boolean} visibility true or false
35437          */
35438         "visibilitychange" : true,
35439         /**
35440          * @event paneladded
35441          * Fires when a panel is added. 
35442          * @param {Roo.LayoutRegion} this
35443          * @param {Roo.ContentPanel} panel The panel
35444          */
35445         "paneladded" : true,
35446         /**
35447          * @event panelremoved
35448          * Fires when a panel is removed. 
35449          * @param {Roo.LayoutRegion} this
35450          * @param {Roo.ContentPanel} panel The panel
35451          */
35452         "panelremoved" : true,
35453         /**
35454          * @event beforecollapse
35455          * Fires when this region before collapse.
35456          * @param {Roo.LayoutRegion} this
35457          */
35458         "beforecollapse" : true,
35459         /**
35460          * @event collapsed
35461          * Fires when this region is collapsed.
35462          * @param {Roo.LayoutRegion} this
35463          */
35464         "collapsed" : true,
35465         /**
35466          * @event expanded
35467          * Fires when this region is expanded.
35468          * @param {Roo.LayoutRegion} this
35469          */
35470         "expanded" : true,
35471         /**
35472          * @event slideshow
35473          * Fires when this region is slid into view.
35474          * @param {Roo.LayoutRegion} this
35475          */
35476         "slideshow" : true,
35477         /**
35478          * @event slidehide
35479          * Fires when this region slides out of view. 
35480          * @param {Roo.LayoutRegion} this
35481          */
35482         "slidehide" : true,
35483         /**
35484          * @event panelactivated
35485          * Fires when a panel is activated. 
35486          * @param {Roo.LayoutRegion} this
35487          * @param {Roo.ContentPanel} panel The activated panel
35488          */
35489         "panelactivated" : true,
35490         /**
35491          * @event resized
35492          * Fires when the user resizes this region. 
35493          * @param {Roo.LayoutRegion} this
35494          * @param {Number} newSize The new size (width for east/west, height for north/south)
35495          */
35496         "resized" : true
35497     };
35498     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35499     this.panels = new Roo.util.MixedCollection();
35500     this.panels.getKey = this.getPanelId.createDelegate(this);
35501     this.box = null;
35502     this.activePanel = null;
35503     // ensure listeners are added...
35504     
35505     if (config.listeners || config.events) {
35506         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35507             listeners : config.listeners || {},
35508             events : config.events || {}
35509         });
35510     }
35511     
35512     if(skipConfig !== true){
35513         this.applyConfig(config);
35514     }
35515 };
35516
35517 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35518 {
35519     getPanelId : function(p){
35520         return p.getId();
35521     },
35522     
35523     applyConfig : function(config){
35524         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35525         this.config = config;
35526         
35527     },
35528     
35529     /**
35530      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35531      * the width, for horizontal (north, south) the height.
35532      * @param {Number} newSize The new width or height
35533      */
35534     resizeTo : function(newSize){
35535         var el = this.el ? this.el :
35536                  (this.activePanel ? this.activePanel.getEl() : null);
35537         if(el){
35538             switch(this.position){
35539                 case "east":
35540                 case "west":
35541                     el.setWidth(newSize);
35542                     this.fireEvent("resized", this, newSize);
35543                 break;
35544                 case "north":
35545                 case "south":
35546                     el.setHeight(newSize);
35547                     this.fireEvent("resized", this, newSize);
35548                 break;                
35549             }
35550         }
35551     },
35552     
35553     getBox : function(){
35554         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35555     },
35556     
35557     getMargins : function(){
35558         return this.margins;
35559     },
35560     
35561     updateBox : function(box){
35562         this.box = box;
35563         var el = this.activePanel.getEl();
35564         el.dom.style.left = box.x + "px";
35565         el.dom.style.top = box.y + "px";
35566         this.activePanel.setSize(box.width, box.height);
35567     },
35568     
35569     /**
35570      * Returns the container element for this region.
35571      * @return {Roo.Element}
35572      */
35573     getEl : function(){
35574         return this.activePanel;
35575     },
35576     
35577     /**
35578      * Returns true if this region is currently visible.
35579      * @return {Boolean}
35580      */
35581     isVisible : function(){
35582         return this.activePanel ? true : false;
35583     },
35584     
35585     setActivePanel : function(panel){
35586         panel = this.getPanel(panel);
35587         if(this.activePanel && this.activePanel != panel){
35588             this.activePanel.setActiveState(false);
35589             this.activePanel.getEl().setLeftTop(-10000,-10000);
35590         }
35591         this.activePanel = panel;
35592         panel.setActiveState(true);
35593         if(this.box){
35594             panel.setSize(this.box.width, this.box.height);
35595         }
35596         this.fireEvent("panelactivated", this, panel);
35597         this.fireEvent("invalidated");
35598     },
35599     
35600     /**
35601      * Show the specified panel.
35602      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35603      * @return {Roo.ContentPanel} The shown panel or null
35604      */
35605     showPanel : function(panel){
35606         panel = this.getPanel(panel);
35607         if(panel){
35608             this.setActivePanel(panel);
35609         }
35610         return panel;
35611     },
35612     
35613     /**
35614      * Get the active panel for this region.
35615      * @return {Roo.ContentPanel} The active panel or null
35616      */
35617     getActivePanel : function(){
35618         return this.activePanel;
35619     },
35620     
35621     /**
35622      * Add the passed ContentPanel(s)
35623      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35624      * @return {Roo.ContentPanel} The panel added (if only one was added)
35625      */
35626     add : function(panel){
35627         if(arguments.length > 1){
35628             for(var i = 0, len = arguments.length; i < len; i++) {
35629                 this.add(arguments[i]);
35630             }
35631             return null;
35632         }
35633         if(this.hasPanel(panel)){
35634             this.showPanel(panel);
35635             return panel;
35636         }
35637         var el = panel.getEl();
35638         if(el.dom.parentNode != this.mgr.el.dom){
35639             this.mgr.el.dom.appendChild(el.dom);
35640         }
35641         if(panel.setRegion){
35642             panel.setRegion(this);
35643         }
35644         this.panels.add(panel);
35645         el.setStyle("position", "absolute");
35646         if(!panel.background){
35647             this.setActivePanel(panel);
35648             if(this.config.initialSize && this.panels.getCount()==1){
35649                 this.resizeTo(this.config.initialSize);
35650             }
35651         }
35652         this.fireEvent("paneladded", this, panel);
35653         return panel;
35654     },
35655     
35656     /**
35657      * Returns true if the panel is in this region.
35658      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35659      * @return {Boolean}
35660      */
35661     hasPanel : function(panel){
35662         if(typeof panel == "object"){ // must be panel obj
35663             panel = panel.getId();
35664         }
35665         return this.getPanel(panel) ? true : false;
35666     },
35667     
35668     /**
35669      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35670      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35671      * @param {Boolean} preservePanel Overrides the config preservePanel option
35672      * @return {Roo.ContentPanel} The panel that was removed
35673      */
35674     remove : function(panel, preservePanel){
35675         panel = this.getPanel(panel);
35676         if(!panel){
35677             return null;
35678         }
35679         var e = {};
35680         this.fireEvent("beforeremove", this, panel, e);
35681         if(e.cancel === true){
35682             return null;
35683         }
35684         var panelId = panel.getId();
35685         this.panels.removeKey(panelId);
35686         return panel;
35687     },
35688     
35689     /**
35690      * Returns the panel specified or null if it's not in this region.
35691      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35692      * @return {Roo.ContentPanel}
35693      */
35694     getPanel : function(id){
35695         if(typeof id == "object"){ // must be panel obj
35696             return id;
35697         }
35698         return this.panels.get(id);
35699     },
35700     
35701     /**
35702      * Returns this regions position (north/south/east/west/center).
35703      * @return {String} 
35704      */
35705     getPosition: function(){
35706         return this.position;    
35707     }
35708 });/*
35709  * Based on:
35710  * Ext JS Library 1.1.1
35711  * Copyright(c) 2006-2007, Ext JS, LLC.
35712  *
35713  * Originally Released Under LGPL - original licence link has changed is not relivant.
35714  *
35715  * Fork - LGPL
35716  * <script type="text/javascript">
35717  */
35718  
35719 /**
35720  * @class Roo.bootstrap.layout.Region
35721  * @extends Roo.bootstrap.layout.Basic
35722  * This class represents a region in a layout manager.
35723  
35724  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35725  * @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})
35726  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35727  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35728  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35729  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35730  * @cfg {String}    title           The title for the region (overrides panel titles)
35731  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35732  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35733  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35734  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35735  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35736  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35737  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35738  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35739  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35740  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35741
35742  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35743  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35744  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35745  * @cfg {Number}    width           For East/West panels
35746  * @cfg {Number}    height          For North/South panels
35747  * @cfg {Boolean}   split           To show the splitter
35748  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35749  * 
35750  * @cfg {string}   cls             Extra CSS classes to add to region
35751  * 
35752  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35753  * @cfg {string}   region  the region that it inhabits..
35754  *
35755
35756  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35757  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35758
35759  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35760  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35761  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35762  */
35763 Roo.bootstrap.layout.Region = function(config)
35764 {
35765     this.applyConfig(config);
35766
35767     var mgr = config.mgr;
35768     var pos = config.region;
35769     config.skipConfig = true;
35770     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35771     
35772     if (mgr.el) {
35773         this.onRender(mgr.el);   
35774     }
35775      
35776     this.visible = true;
35777     this.collapsed = false;
35778     this.unrendered_panels = [];
35779 };
35780
35781 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35782
35783     position: '', // set by wrapper (eg. north/south etc..)
35784     unrendered_panels : null,  // unrendered panels.
35785     createBody : function(){
35786         /** This region's body element 
35787         * @type Roo.Element */
35788         this.bodyEl = this.el.createChild({
35789                 tag: "div",
35790                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35791         });
35792     },
35793
35794     onRender: function(ctr, pos)
35795     {
35796         var dh = Roo.DomHelper;
35797         /** This region's container element 
35798         * @type Roo.Element */
35799         this.el = dh.append(ctr.dom, {
35800                 tag: "div",
35801                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35802             }, true);
35803         /** This region's title element 
35804         * @type Roo.Element */
35805     
35806         this.titleEl = dh.append(this.el.dom,
35807             {
35808                     tag: "div",
35809                     unselectable: "on",
35810                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35811                     children:[
35812                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35813                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35814                     ]}, true);
35815         
35816         this.titleEl.enableDisplayMode();
35817         /** This region's title text element 
35818         * @type HTMLElement */
35819         this.titleTextEl = this.titleEl.dom.firstChild;
35820         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35821         /*
35822         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35823         this.closeBtn.enableDisplayMode();
35824         this.closeBtn.on("click", this.closeClicked, this);
35825         this.closeBtn.hide();
35826     */
35827         this.createBody(this.config);
35828         if(this.config.hideWhenEmpty){
35829             this.hide();
35830             this.on("paneladded", this.validateVisibility, this);
35831             this.on("panelremoved", this.validateVisibility, this);
35832         }
35833         if(this.autoScroll){
35834             this.bodyEl.setStyle("overflow", "auto");
35835         }else{
35836             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35837         }
35838         //if(c.titlebar !== false){
35839             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35840                 this.titleEl.hide();
35841             }else{
35842                 this.titleEl.show();
35843                 if(this.config.title){
35844                     this.titleTextEl.innerHTML = this.config.title;
35845                 }
35846             }
35847         //}
35848         if(this.config.collapsed){
35849             this.collapse(true);
35850         }
35851         if(this.config.hidden){
35852             this.hide();
35853         }
35854         
35855         if (this.unrendered_panels && this.unrendered_panels.length) {
35856             for (var i =0;i< this.unrendered_panels.length; i++) {
35857                 this.add(this.unrendered_panels[i]);
35858             }
35859             this.unrendered_panels = null;
35860             
35861         }
35862         
35863     },
35864     
35865     applyConfig : function(c)
35866     {
35867         /*
35868          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35869             var dh = Roo.DomHelper;
35870             if(c.titlebar !== false){
35871                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35872                 this.collapseBtn.on("click", this.collapse, this);
35873                 this.collapseBtn.enableDisplayMode();
35874                 /*
35875                 if(c.showPin === true || this.showPin){
35876                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35877                     this.stickBtn.enableDisplayMode();
35878                     this.stickBtn.on("click", this.expand, this);
35879                     this.stickBtn.hide();
35880                 }
35881                 
35882             }
35883             */
35884             /** This region's collapsed element
35885             * @type Roo.Element */
35886             /*
35887              *
35888             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35889                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35890             ]}, true);
35891             
35892             if(c.floatable !== false){
35893                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35894                this.collapsedEl.on("click", this.collapseClick, this);
35895             }
35896
35897             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35898                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35899                    id: "message", unselectable: "on", style:{"float":"left"}});
35900                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35901              }
35902             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35903             this.expandBtn.on("click", this.expand, this);
35904             
35905         }
35906         
35907         if(this.collapseBtn){
35908             this.collapseBtn.setVisible(c.collapsible == true);
35909         }
35910         
35911         this.cmargins = c.cmargins || this.cmargins ||
35912                          (this.position == "west" || this.position == "east" ?
35913                              {top: 0, left: 2, right:2, bottom: 0} :
35914                              {top: 2, left: 0, right:0, bottom: 2});
35915         */
35916         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35917         
35918         
35919         this.bottomTabs = c.tabPosition != "top";
35920         
35921         this.autoScroll = c.autoScroll || false;
35922         
35923         
35924        
35925         
35926         this.duration = c.duration || .30;
35927         this.slideDuration = c.slideDuration || .45;
35928         this.config = c;
35929        
35930     },
35931     /**
35932      * Returns true if this region is currently visible.
35933      * @return {Boolean}
35934      */
35935     isVisible : function(){
35936         return this.visible;
35937     },
35938
35939     /**
35940      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35941      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35942      */
35943     //setCollapsedTitle : function(title){
35944     //    title = title || "&#160;";
35945      //   if(this.collapsedTitleTextEl){
35946       //      this.collapsedTitleTextEl.innerHTML = title;
35947        // }
35948     //},
35949
35950     getBox : function(){
35951         var b;
35952       //  if(!this.collapsed){
35953             b = this.el.getBox(false, true);
35954        // }else{
35955           //  b = this.collapsedEl.getBox(false, true);
35956         //}
35957         return b;
35958     },
35959
35960     getMargins : function(){
35961         return this.margins;
35962         //return this.collapsed ? this.cmargins : this.margins;
35963     },
35964 /*
35965     highlight : function(){
35966         this.el.addClass("x-layout-panel-dragover");
35967     },
35968
35969     unhighlight : function(){
35970         this.el.removeClass("x-layout-panel-dragover");
35971     },
35972 */
35973     updateBox : function(box)
35974     {
35975         if (!this.bodyEl) {
35976             return; // not rendered yet..
35977         }
35978         
35979         this.box = box;
35980         if(!this.collapsed){
35981             this.el.dom.style.left = box.x + "px";
35982             this.el.dom.style.top = box.y + "px";
35983             this.updateBody(box.width, box.height);
35984         }else{
35985             this.collapsedEl.dom.style.left = box.x + "px";
35986             this.collapsedEl.dom.style.top = box.y + "px";
35987             this.collapsedEl.setSize(box.width, box.height);
35988         }
35989         if(this.tabs){
35990             this.tabs.autoSizeTabs();
35991         }
35992     },
35993
35994     updateBody : function(w, h)
35995     {
35996         if(w !== null){
35997             this.el.setWidth(w);
35998             w -= this.el.getBorderWidth("rl");
35999             if(this.config.adjustments){
36000                 w += this.config.adjustments[0];
36001             }
36002         }
36003         if(h !== null && h > 0){
36004             this.el.setHeight(h);
36005             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36006             h -= this.el.getBorderWidth("tb");
36007             if(this.config.adjustments){
36008                 h += this.config.adjustments[1];
36009             }
36010             this.bodyEl.setHeight(h);
36011             if(this.tabs){
36012                 h = this.tabs.syncHeight(h);
36013             }
36014         }
36015         if(this.panelSize){
36016             w = w !== null ? w : this.panelSize.width;
36017             h = h !== null ? h : this.panelSize.height;
36018         }
36019         if(this.activePanel){
36020             var el = this.activePanel.getEl();
36021             w = w !== null ? w : el.getWidth();
36022             h = h !== null ? h : el.getHeight();
36023             this.panelSize = {width: w, height: h};
36024             this.activePanel.setSize(w, h);
36025         }
36026         if(Roo.isIE && this.tabs){
36027             this.tabs.el.repaint();
36028         }
36029     },
36030
36031     /**
36032      * Returns the container element for this region.
36033      * @return {Roo.Element}
36034      */
36035     getEl : function(){
36036         return this.el;
36037     },
36038
36039     /**
36040      * Hides this region.
36041      */
36042     hide : function(){
36043         //if(!this.collapsed){
36044             this.el.dom.style.left = "-2000px";
36045             this.el.hide();
36046         //}else{
36047          //   this.collapsedEl.dom.style.left = "-2000px";
36048          //   this.collapsedEl.hide();
36049        // }
36050         this.visible = false;
36051         this.fireEvent("visibilitychange", this, false);
36052     },
36053
36054     /**
36055      * Shows this region if it was previously hidden.
36056      */
36057     show : function(){
36058         //if(!this.collapsed){
36059             this.el.show();
36060         //}else{
36061         //    this.collapsedEl.show();
36062        // }
36063         this.visible = true;
36064         this.fireEvent("visibilitychange", this, true);
36065     },
36066 /*
36067     closeClicked : function(){
36068         if(this.activePanel){
36069             this.remove(this.activePanel);
36070         }
36071     },
36072
36073     collapseClick : function(e){
36074         if(this.isSlid){
36075            e.stopPropagation();
36076            this.slideIn();
36077         }else{
36078            e.stopPropagation();
36079            this.slideOut();
36080         }
36081     },
36082 */
36083     /**
36084      * Collapses this region.
36085      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36086      */
36087     /*
36088     collapse : function(skipAnim, skipCheck = false){
36089         if(this.collapsed) {
36090             return;
36091         }
36092         
36093         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36094             
36095             this.collapsed = true;
36096             if(this.split){
36097                 this.split.el.hide();
36098             }
36099             if(this.config.animate && skipAnim !== true){
36100                 this.fireEvent("invalidated", this);
36101                 this.animateCollapse();
36102             }else{
36103                 this.el.setLocation(-20000,-20000);
36104                 this.el.hide();
36105                 this.collapsedEl.show();
36106                 this.fireEvent("collapsed", this);
36107                 this.fireEvent("invalidated", this);
36108             }
36109         }
36110         
36111     },
36112 */
36113     animateCollapse : function(){
36114         // overridden
36115     },
36116
36117     /**
36118      * Expands this region if it was previously collapsed.
36119      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36120      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36121      */
36122     /*
36123     expand : function(e, skipAnim){
36124         if(e) {
36125             e.stopPropagation();
36126         }
36127         if(!this.collapsed || this.el.hasActiveFx()) {
36128             return;
36129         }
36130         if(this.isSlid){
36131             this.afterSlideIn();
36132             skipAnim = true;
36133         }
36134         this.collapsed = false;
36135         if(this.config.animate && skipAnim !== true){
36136             this.animateExpand();
36137         }else{
36138             this.el.show();
36139             if(this.split){
36140                 this.split.el.show();
36141             }
36142             this.collapsedEl.setLocation(-2000,-2000);
36143             this.collapsedEl.hide();
36144             this.fireEvent("invalidated", this);
36145             this.fireEvent("expanded", this);
36146         }
36147     },
36148 */
36149     animateExpand : function(){
36150         // overridden
36151     },
36152
36153     initTabs : function()
36154     {
36155         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36156         
36157         var ts = new Roo.bootstrap.panel.Tabs({
36158                 el: this.bodyEl.dom,
36159                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36160                 disableTooltips: this.config.disableTabTips,
36161                 toolbar : this.config.toolbar
36162             });
36163         
36164         if(this.config.hideTabs){
36165             ts.stripWrap.setDisplayed(false);
36166         }
36167         this.tabs = ts;
36168         ts.resizeTabs = this.config.resizeTabs === true;
36169         ts.minTabWidth = this.config.minTabWidth || 40;
36170         ts.maxTabWidth = this.config.maxTabWidth || 250;
36171         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36172         ts.monitorResize = false;
36173         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36174         ts.bodyEl.addClass('roo-layout-tabs-body');
36175         this.panels.each(this.initPanelAsTab, this);
36176     },
36177
36178     initPanelAsTab : function(panel){
36179         var ti = this.tabs.addTab(
36180             panel.getEl().id,
36181             panel.getTitle(),
36182             null,
36183             this.config.closeOnTab && panel.isClosable(),
36184             panel.tpl
36185         );
36186         if(panel.tabTip !== undefined){
36187             ti.setTooltip(panel.tabTip);
36188         }
36189         ti.on("activate", function(){
36190               this.setActivePanel(panel);
36191         }, this);
36192         
36193         if(this.config.closeOnTab){
36194             ti.on("beforeclose", function(t, e){
36195                 e.cancel = true;
36196                 this.remove(panel);
36197             }, this);
36198         }
36199         
36200         panel.tabItem = ti;
36201         
36202         return ti;
36203     },
36204
36205     updatePanelTitle : function(panel, title)
36206     {
36207         if(this.activePanel == panel){
36208             this.updateTitle(title);
36209         }
36210         if(this.tabs){
36211             var ti = this.tabs.getTab(panel.getEl().id);
36212             ti.setText(title);
36213             if(panel.tabTip !== undefined){
36214                 ti.setTooltip(panel.tabTip);
36215             }
36216         }
36217     },
36218
36219     updateTitle : function(title){
36220         if(this.titleTextEl && !this.config.title){
36221             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36222         }
36223     },
36224
36225     setActivePanel : function(panel)
36226     {
36227         panel = this.getPanel(panel);
36228         if(this.activePanel && this.activePanel != panel){
36229             if(this.activePanel.setActiveState(false) === false){
36230                 return;
36231             }
36232         }
36233         this.activePanel = panel;
36234         panel.setActiveState(true);
36235         if(this.panelSize){
36236             panel.setSize(this.panelSize.width, this.panelSize.height);
36237         }
36238         if(this.closeBtn){
36239             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36240         }
36241         this.updateTitle(panel.getTitle());
36242         if(this.tabs){
36243             this.fireEvent("invalidated", this);
36244         }
36245         this.fireEvent("panelactivated", this, panel);
36246     },
36247
36248     /**
36249      * Shows the specified panel.
36250      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36251      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36252      */
36253     showPanel : function(panel)
36254     {
36255         panel = this.getPanel(panel);
36256         if(panel){
36257             if(this.tabs){
36258                 var tab = this.tabs.getTab(panel.getEl().id);
36259                 if(tab.isHidden()){
36260                     this.tabs.unhideTab(tab.id);
36261                 }
36262                 tab.activate();
36263             }else{
36264                 this.setActivePanel(panel);
36265             }
36266         }
36267         return panel;
36268     },
36269
36270     /**
36271      * Get the active panel for this region.
36272      * @return {Roo.ContentPanel} The active panel or null
36273      */
36274     getActivePanel : function(){
36275         return this.activePanel;
36276     },
36277
36278     validateVisibility : function(){
36279         if(this.panels.getCount() < 1){
36280             this.updateTitle("&#160;");
36281             this.closeBtn.hide();
36282             this.hide();
36283         }else{
36284             if(!this.isVisible()){
36285                 this.show();
36286             }
36287         }
36288     },
36289
36290     /**
36291      * Adds the passed ContentPanel(s) to this region.
36292      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36293      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36294      */
36295     add : function(panel)
36296     {
36297         if(arguments.length > 1){
36298             for(var i = 0, len = arguments.length; i < len; i++) {
36299                 this.add(arguments[i]);
36300             }
36301             return null;
36302         }
36303         
36304         // if we have not been rendered yet, then we can not really do much of this..
36305         if (!this.bodyEl) {
36306             this.unrendered_panels.push(panel);
36307             return panel;
36308         }
36309         
36310         
36311         
36312         
36313         if(this.hasPanel(panel)){
36314             this.showPanel(panel);
36315             return panel;
36316         }
36317         panel.setRegion(this);
36318         this.panels.add(panel);
36319        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36320             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36321             // and hide them... ???
36322             this.bodyEl.dom.appendChild(panel.getEl().dom);
36323             if(panel.background !== true){
36324                 this.setActivePanel(panel);
36325             }
36326             this.fireEvent("paneladded", this, panel);
36327             return panel;
36328         }
36329         */
36330         if(!this.tabs){
36331             this.initTabs();
36332         }else{
36333             this.initPanelAsTab(panel);
36334         }
36335         
36336         
36337         if(panel.background !== true){
36338             this.tabs.activate(panel.getEl().id);
36339         }
36340         this.fireEvent("paneladded", this, panel);
36341         return panel;
36342     },
36343
36344     /**
36345      * Hides the tab for the specified panel.
36346      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36347      */
36348     hidePanel : function(panel){
36349         if(this.tabs && (panel = this.getPanel(panel))){
36350             this.tabs.hideTab(panel.getEl().id);
36351         }
36352     },
36353
36354     /**
36355      * Unhides the tab for a previously hidden panel.
36356      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36357      */
36358     unhidePanel : function(panel){
36359         if(this.tabs && (panel = this.getPanel(panel))){
36360             this.tabs.unhideTab(panel.getEl().id);
36361         }
36362     },
36363
36364     clearPanels : function(){
36365         while(this.panels.getCount() > 0){
36366              this.remove(this.panels.first());
36367         }
36368     },
36369
36370     /**
36371      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36372      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36373      * @param {Boolean} preservePanel Overrides the config preservePanel option
36374      * @return {Roo.ContentPanel} The panel that was removed
36375      */
36376     remove : function(panel, preservePanel)
36377     {
36378         panel = this.getPanel(panel);
36379         if(!panel){
36380             return null;
36381         }
36382         var e = {};
36383         this.fireEvent("beforeremove", this, panel, e);
36384         if(e.cancel === true){
36385             return null;
36386         }
36387         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36388         var panelId = panel.getId();
36389         this.panels.removeKey(panelId);
36390         if(preservePanel){
36391             document.body.appendChild(panel.getEl().dom);
36392         }
36393         if(this.tabs){
36394             this.tabs.removeTab(panel.getEl().id);
36395         }else if (!preservePanel){
36396             this.bodyEl.dom.removeChild(panel.getEl().dom);
36397         }
36398         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36399             var p = this.panels.first();
36400             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36401             tempEl.appendChild(p.getEl().dom);
36402             this.bodyEl.update("");
36403             this.bodyEl.dom.appendChild(p.getEl().dom);
36404             tempEl = null;
36405             this.updateTitle(p.getTitle());
36406             this.tabs = null;
36407             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36408             this.setActivePanel(p);
36409         }
36410         panel.setRegion(null);
36411         if(this.activePanel == panel){
36412             this.activePanel = null;
36413         }
36414         if(this.config.autoDestroy !== false && preservePanel !== true){
36415             try{panel.destroy();}catch(e){}
36416         }
36417         this.fireEvent("panelremoved", this, panel);
36418         return panel;
36419     },
36420
36421     /**
36422      * Returns the TabPanel component used by this region
36423      * @return {Roo.TabPanel}
36424      */
36425     getTabs : function(){
36426         return this.tabs;
36427     },
36428
36429     createTool : function(parentEl, className){
36430         var btn = Roo.DomHelper.append(parentEl, {
36431             tag: "div",
36432             cls: "x-layout-tools-button",
36433             children: [ {
36434                 tag: "div",
36435                 cls: "roo-layout-tools-button-inner " + className,
36436                 html: "&#160;"
36437             }]
36438         }, true);
36439         btn.addClassOnOver("roo-layout-tools-button-over");
36440         return btn;
36441     }
36442 });/*
36443  * Based on:
36444  * Ext JS Library 1.1.1
36445  * Copyright(c) 2006-2007, Ext JS, LLC.
36446  *
36447  * Originally Released Under LGPL - original licence link has changed is not relivant.
36448  *
36449  * Fork - LGPL
36450  * <script type="text/javascript">
36451  */
36452  
36453
36454
36455 /**
36456  * @class Roo.SplitLayoutRegion
36457  * @extends Roo.LayoutRegion
36458  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36459  */
36460 Roo.bootstrap.layout.Split = function(config){
36461     this.cursor = config.cursor;
36462     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36463 };
36464
36465 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36466 {
36467     splitTip : "Drag to resize.",
36468     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36469     useSplitTips : false,
36470
36471     applyConfig : function(config){
36472         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36473     },
36474     
36475     onRender : function(ctr,pos) {
36476         
36477         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36478         if(!this.config.split){
36479             return;
36480         }
36481         if(!this.split){
36482             
36483             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36484                             tag: "div",
36485                             id: this.el.id + "-split",
36486                             cls: "roo-layout-split roo-layout-split-"+this.position,
36487                             html: "&#160;"
36488             });
36489             /** The SplitBar for this region 
36490             * @type Roo.SplitBar */
36491             // does not exist yet...
36492             Roo.log([this.position, this.orientation]);
36493             
36494             this.split = new Roo.bootstrap.SplitBar({
36495                 dragElement : splitEl,
36496                 resizingElement: this.el,
36497                 orientation : this.orientation
36498             });
36499             
36500             this.split.on("moved", this.onSplitMove, this);
36501             this.split.useShim = this.config.useShim === true;
36502             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36503             if(this.useSplitTips){
36504                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36505             }
36506             //if(config.collapsible){
36507             //    this.split.el.on("dblclick", this.collapse,  this);
36508             //}
36509         }
36510         if(typeof this.config.minSize != "undefined"){
36511             this.split.minSize = this.config.minSize;
36512         }
36513         if(typeof this.config.maxSize != "undefined"){
36514             this.split.maxSize = this.config.maxSize;
36515         }
36516         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36517             this.hideSplitter();
36518         }
36519         
36520     },
36521
36522     getHMaxSize : function(){
36523          var cmax = this.config.maxSize || 10000;
36524          var center = this.mgr.getRegion("center");
36525          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36526     },
36527
36528     getVMaxSize : function(){
36529          var cmax = this.config.maxSize || 10000;
36530          var center = this.mgr.getRegion("center");
36531          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36532     },
36533
36534     onSplitMove : function(split, newSize){
36535         this.fireEvent("resized", this, newSize);
36536     },
36537     
36538     /** 
36539      * Returns the {@link Roo.SplitBar} for this region.
36540      * @return {Roo.SplitBar}
36541      */
36542     getSplitBar : function(){
36543         return this.split;
36544     },
36545     
36546     hide : function(){
36547         this.hideSplitter();
36548         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36549     },
36550
36551     hideSplitter : function(){
36552         if(this.split){
36553             this.split.el.setLocation(-2000,-2000);
36554             this.split.el.hide();
36555         }
36556     },
36557
36558     show : function(){
36559         if(this.split){
36560             this.split.el.show();
36561         }
36562         Roo.bootstrap.layout.Split.superclass.show.call(this);
36563     },
36564     
36565     beforeSlide: function(){
36566         if(Roo.isGecko){// firefox overflow auto bug workaround
36567             this.bodyEl.clip();
36568             if(this.tabs) {
36569                 this.tabs.bodyEl.clip();
36570             }
36571             if(this.activePanel){
36572                 this.activePanel.getEl().clip();
36573                 
36574                 if(this.activePanel.beforeSlide){
36575                     this.activePanel.beforeSlide();
36576                 }
36577             }
36578         }
36579     },
36580     
36581     afterSlide : function(){
36582         if(Roo.isGecko){// firefox overflow auto bug workaround
36583             this.bodyEl.unclip();
36584             if(this.tabs) {
36585                 this.tabs.bodyEl.unclip();
36586             }
36587             if(this.activePanel){
36588                 this.activePanel.getEl().unclip();
36589                 if(this.activePanel.afterSlide){
36590                     this.activePanel.afterSlide();
36591                 }
36592             }
36593         }
36594     },
36595
36596     initAutoHide : function(){
36597         if(this.autoHide !== false){
36598             if(!this.autoHideHd){
36599                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36600                 this.autoHideHd = {
36601                     "mouseout": function(e){
36602                         if(!e.within(this.el, true)){
36603                             st.delay(500);
36604                         }
36605                     },
36606                     "mouseover" : function(e){
36607                         st.cancel();
36608                     },
36609                     scope : this
36610                 };
36611             }
36612             this.el.on(this.autoHideHd);
36613         }
36614     },
36615
36616     clearAutoHide : function(){
36617         if(this.autoHide !== false){
36618             this.el.un("mouseout", this.autoHideHd.mouseout);
36619             this.el.un("mouseover", this.autoHideHd.mouseover);
36620         }
36621     },
36622
36623     clearMonitor : function(){
36624         Roo.get(document).un("click", this.slideInIf, this);
36625     },
36626
36627     // these names are backwards but not changed for compat
36628     slideOut : function(){
36629         if(this.isSlid || this.el.hasActiveFx()){
36630             return;
36631         }
36632         this.isSlid = true;
36633         if(this.collapseBtn){
36634             this.collapseBtn.hide();
36635         }
36636         this.closeBtnState = this.closeBtn.getStyle('display');
36637         this.closeBtn.hide();
36638         if(this.stickBtn){
36639             this.stickBtn.show();
36640         }
36641         this.el.show();
36642         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36643         this.beforeSlide();
36644         this.el.setStyle("z-index", 10001);
36645         this.el.slideIn(this.getSlideAnchor(), {
36646             callback: function(){
36647                 this.afterSlide();
36648                 this.initAutoHide();
36649                 Roo.get(document).on("click", this.slideInIf, this);
36650                 this.fireEvent("slideshow", this);
36651             },
36652             scope: this,
36653             block: true
36654         });
36655     },
36656
36657     afterSlideIn : function(){
36658         this.clearAutoHide();
36659         this.isSlid = false;
36660         this.clearMonitor();
36661         this.el.setStyle("z-index", "");
36662         if(this.collapseBtn){
36663             this.collapseBtn.show();
36664         }
36665         this.closeBtn.setStyle('display', this.closeBtnState);
36666         if(this.stickBtn){
36667             this.stickBtn.hide();
36668         }
36669         this.fireEvent("slidehide", this);
36670     },
36671
36672     slideIn : function(cb){
36673         if(!this.isSlid || this.el.hasActiveFx()){
36674             Roo.callback(cb);
36675             return;
36676         }
36677         this.isSlid = false;
36678         this.beforeSlide();
36679         this.el.slideOut(this.getSlideAnchor(), {
36680             callback: function(){
36681                 this.el.setLeftTop(-10000, -10000);
36682                 this.afterSlide();
36683                 this.afterSlideIn();
36684                 Roo.callback(cb);
36685             },
36686             scope: this,
36687             block: true
36688         });
36689     },
36690     
36691     slideInIf : function(e){
36692         if(!e.within(this.el)){
36693             this.slideIn();
36694         }
36695     },
36696
36697     animateCollapse : function(){
36698         this.beforeSlide();
36699         this.el.setStyle("z-index", 20000);
36700         var anchor = this.getSlideAnchor();
36701         this.el.slideOut(anchor, {
36702             callback : function(){
36703                 this.el.setStyle("z-index", "");
36704                 this.collapsedEl.slideIn(anchor, {duration:.3});
36705                 this.afterSlide();
36706                 this.el.setLocation(-10000,-10000);
36707                 this.el.hide();
36708                 this.fireEvent("collapsed", this);
36709             },
36710             scope: this,
36711             block: true
36712         });
36713     },
36714
36715     animateExpand : function(){
36716         this.beforeSlide();
36717         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36718         this.el.setStyle("z-index", 20000);
36719         this.collapsedEl.hide({
36720             duration:.1
36721         });
36722         this.el.slideIn(this.getSlideAnchor(), {
36723             callback : function(){
36724                 this.el.setStyle("z-index", "");
36725                 this.afterSlide();
36726                 if(this.split){
36727                     this.split.el.show();
36728                 }
36729                 this.fireEvent("invalidated", this);
36730                 this.fireEvent("expanded", this);
36731             },
36732             scope: this,
36733             block: true
36734         });
36735     },
36736
36737     anchors : {
36738         "west" : "left",
36739         "east" : "right",
36740         "north" : "top",
36741         "south" : "bottom"
36742     },
36743
36744     sanchors : {
36745         "west" : "l",
36746         "east" : "r",
36747         "north" : "t",
36748         "south" : "b"
36749     },
36750
36751     canchors : {
36752         "west" : "tl-tr",
36753         "east" : "tr-tl",
36754         "north" : "tl-bl",
36755         "south" : "bl-tl"
36756     },
36757
36758     getAnchor : function(){
36759         return this.anchors[this.position];
36760     },
36761
36762     getCollapseAnchor : function(){
36763         return this.canchors[this.position];
36764     },
36765
36766     getSlideAnchor : function(){
36767         return this.sanchors[this.position];
36768     },
36769
36770     getAlignAdj : function(){
36771         var cm = this.cmargins;
36772         switch(this.position){
36773             case "west":
36774                 return [0, 0];
36775             break;
36776             case "east":
36777                 return [0, 0];
36778             break;
36779             case "north":
36780                 return [0, 0];
36781             break;
36782             case "south":
36783                 return [0, 0];
36784             break;
36785         }
36786     },
36787
36788     getExpandAdj : function(){
36789         var c = this.collapsedEl, cm = this.cmargins;
36790         switch(this.position){
36791             case "west":
36792                 return [-(cm.right+c.getWidth()+cm.left), 0];
36793             break;
36794             case "east":
36795                 return [cm.right+c.getWidth()+cm.left, 0];
36796             break;
36797             case "north":
36798                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36799             break;
36800             case "south":
36801                 return [0, cm.top+cm.bottom+c.getHeight()];
36802             break;
36803         }
36804     }
36805 });/*
36806  * Based on:
36807  * Ext JS Library 1.1.1
36808  * Copyright(c) 2006-2007, Ext JS, LLC.
36809  *
36810  * Originally Released Under LGPL - original licence link has changed is not relivant.
36811  *
36812  * Fork - LGPL
36813  * <script type="text/javascript">
36814  */
36815 /*
36816  * These classes are private internal classes
36817  */
36818 Roo.bootstrap.layout.Center = function(config){
36819     config.region = "center";
36820     Roo.bootstrap.layout.Region.call(this, config);
36821     this.visible = true;
36822     this.minWidth = config.minWidth || 20;
36823     this.minHeight = config.minHeight || 20;
36824 };
36825
36826 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36827     hide : function(){
36828         // center panel can't be hidden
36829     },
36830     
36831     show : function(){
36832         // center panel can't be hidden
36833     },
36834     
36835     getMinWidth: function(){
36836         return this.minWidth;
36837     },
36838     
36839     getMinHeight: function(){
36840         return this.minHeight;
36841     }
36842 });
36843
36844
36845
36846
36847  
36848
36849
36850
36851
36852
36853 Roo.bootstrap.layout.North = function(config)
36854 {
36855     config.region = 'north';
36856     config.cursor = 'n-resize';
36857     
36858     Roo.bootstrap.layout.Split.call(this, config);
36859     
36860     
36861     if(this.split){
36862         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36863         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36864         this.split.el.addClass("roo-layout-split-v");
36865     }
36866     var size = config.initialSize || config.height;
36867     if(typeof size != "undefined"){
36868         this.el.setHeight(size);
36869     }
36870 };
36871 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36872 {
36873     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36874     
36875     
36876     
36877     getBox : function(){
36878         if(this.collapsed){
36879             return this.collapsedEl.getBox();
36880         }
36881         var box = this.el.getBox();
36882         if(this.split){
36883             box.height += this.split.el.getHeight();
36884         }
36885         return box;
36886     },
36887     
36888     updateBox : function(box){
36889         if(this.split && !this.collapsed){
36890             box.height -= this.split.el.getHeight();
36891             this.split.el.setLeft(box.x);
36892             this.split.el.setTop(box.y+box.height);
36893             this.split.el.setWidth(box.width);
36894         }
36895         if(this.collapsed){
36896             this.updateBody(box.width, null);
36897         }
36898         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36899     }
36900 });
36901
36902
36903
36904
36905
36906 Roo.bootstrap.layout.South = function(config){
36907     config.region = 'south';
36908     config.cursor = 's-resize';
36909     Roo.bootstrap.layout.Split.call(this, config);
36910     if(this.split){
36911         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36912         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36913         this.split.el.addClass("roo-layout-split-v");
36914     }
36915     var size = config.initialSize || config.height;
36916     if(typeof size != "undefined"){
36917         this.el.setHeight(size);
36918     }
36919 };
36920
36921 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36922     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36923     getBox : function(){
36924         if(this.collapsed){
36925             return this.collapsedEl.getBox();
36926         }
36927         var box = this.el.getBox();
36928         if(this.split){
36929             var sh = this.split.el.getHeight();
36930             box.height += sh;
36931             box.y -= sh;
36932         }
36933         return box;
36934     },
36935     
36936     updateBox : function(box){
36937         if(this.split && !this.collapsed){
36938             var sh = this.split.el.getHeight();
36939             box.height -= sh;
36940             box.y += sh;
36941             this.split.el.setLeft(box.x);
36942             this.split.el.setTop(box.y-sh);
36943             this.split.el.setWidth(box.width);
36944         }
36945         if(this.collapsed){
36946             this.updateBody(box.width, null);
36947         }
36948         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36949     }
36950 });
36951
36952 Roo.bootstrap.layout.East = function(config){
36953     config.region = "east";
36954     config.cursor = "e-resize";
36955     Roo.bootstrap.layout.Split.call(this, config);
36956     if(this.split){
36957         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36958         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36959         this.split.el.addClass("roo-layout-split-h");
36960     }
36961     var size = config.initialSize || config.width;
36962     if(typeof size != "undefined"){
36963         this.el.setWidth(size);
36964     }
36965 };
36966 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36967     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36968     getBox : function(){
36969         if(this.collapsed){
36970             return this.collapsedEl.getBox();
36971         }
36972         var box = this.el.getBox();
36973         if(this.split){
36974             var sw = this.split.el.getWidth();
36975             box.width += sw;
36976             box.x -= sw;
36977         }
36978         return box;
36979     },
36980
36981     updateBox : function(box){
36982         if(this.split && !this.collapsed){
36983             var sw = this.split.el.getWidth();
36984             box.width -= sw;
36985             this.split.el.setLeft(box.x);
36986             this.split.el.setTop(box.y);
36987             this.split.el.setHeight(box.height);
36988             box.x += sw;
36989         }
36990         if(this.collapsed){
36991             this.updateBody(null, box.height);
36992         }
36993         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36994     }
36995 });
36996
36997 Roo.bootstrap.layout.West = function(config){
36998     config.region = "west";
36999     config.cursor = "w-resize";
37000     
37001     Roo.bootstrap.layout.Split.call(this, config);
37002     if(this.split){
37003         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37004         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37005         this.split.el.addClass("roo-layout-split-h");
37006     }
37007     
37008 };
37009 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37010     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37011     
37012     onRender: function(ctr, pos)
37013     {
37014         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37015         var size = this.config.initialSize || this.config.width;
37016         if(typeof size != "undefined"){
37017             this.el.setWidth(size);
37018         }
37019     },
37020     
37021     getBox : function(){
37022         if(this.collapsed){
37023             return this.collapsedEl.getBox();
37024         }
37025         var box = this.el.getBox();
37026         if(this.split){
37027             box.width += this.split.el.getWidth();
37028         }
37029         return box;
37030     },
37031     
37032     updateBox : function(box){
37033         if(this.split && !this.collapsed){
37034             var sw = this.split.el.getWidth();
37035             box.width -= sw;
37036             this.split.el.setLeft(box.x+box.width);
37037             this.split.el.setTop(box.y);
37038             this.split.el.setHeight(box.height);
37039         }
37040         if(this.collapsed){
37041             this.updateBody(null, box.height);
37042         }
37043         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37044     }
37045 });
37046 Roo.namespace("Roo.bootstrap.panel");/*
37047  * Based on:
37048  * Ext JS Library 1.1.1
37049  * Copyright(c) 2006-2007, Ext JS, LLC.
37050  *
37051  * Originally Released Under LGPL - original licence link has changed is not relivant.
37052  *
37053  * Fork - LGPL
37054  * <script type="text/javascript">
37055  */
37056 /**
37057  * @class Roo.ContentPanel
37058  * @extends Roo.util.Observable
37059  * A basic ContentPanel element.
37060  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37061  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37062  * @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
37063  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37064  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37065  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37066  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37067  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37068  * @cfg {String} title          The title for this panel
37069  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37070  * @cfg {String} url            Calls {@link #setUrl} with this value
37071  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37072  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37073  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37074  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37075  * @cfg {Boolean} badges render the badges
37076
37077  * @constructor
37078  * Create a new ContentPanel.
37079  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37080  * @param {String/Object} config A string to set only the title or a config object
37081  * @param {String} content (optional) Set the HTML content for this panel
37082  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37083  */
37084 Roo.bootstrap.panel.Content = function( config){
37085     
37086     this.tpl = config.tpl || false;
37087     
37088     var el = config.el;
37089     var content = config.content;
37090
37091     if(config.autoCreate){ // xtype is available if this is called from factory
37092         el = Roo.id();
37093     }
37094     this.el = Roo.get(el);
37095     if(!this.el && config && config.autoCreate){
37096         if(typeof config.autoCreate == "object"){
37097             if(!config.autoCreate.id){
37098                 config.autoCreate.id = config.id||el;
37099             }
37100             this.el = Roo.DomHelper.append(document.body,
37101                         config.autoCreate, true);
37102         }else{
37103             var elcfg =  {   tag: "div",
37104                             cls: "roo-layout-inactive-content",
37105                             id: config.id||el
37106                             };
37107             if (config.html) {
37108                 elcfg.html = config.html;
37109                 
37110             }
37111                         
37112             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37113         }
37114     } 
37115     this.closable = false;
37116     this.loaded = false;
37117     this.active = false;
37118    
37119       
37120     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37121         
37122         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37123         
37124         this.wrapEl = this.el; //this.el.wrap();
37125         var ti = [];
37126         if (config.toolbar.items) {
37127             ti = config.toolbar.items ;
37128             delete config.toolbar.items ;
37129         }
37130         
37131         var nitems = [];
37132         this.toolbar.render(this.wrapEl, 'before');
37133         for(var i =0;i < ti.length;i++) {
37134           //  Roo.log(['add child', items[i]]);
37135             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37136         }
37137         this.toolbar.items = nitems;
37138         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37139         delete config.toolbar;
37140         
37141     }
37142     /*
37143     // xtype created footer. - not sure if will work as we normally have to render first..
37144     if (this.footer && !this.footer.el && this.footer.xtype) {
37145         if (!this.wrapEl) {
37146             this.wrapEl = this.el.wrap();
37147         }
37148     
37149         this.footer.container = this.wrapEl.createChild();
37150          
37151         this.footer = Roo.factory(this.footer, Roo);
37152         
37153     }
37154     */
37155     
37156      if(typeof config == "string"){
37157         this.title = config;
37158     }else{
37159         Roo.apply(this, config);
37160     }
37161     
37162     if(this.resizeEl){
37163         this.resizeEl = Roo.get(this.resizeEl, true);
37164     }else{
37165         this.resizeEl = this.el;
37166     }
37167     // handle view.xtype
37168     
37169  
37170     
37171     
37172     this.addEvents({
37173         /**
37174          * @event activate
37175          * Fires when this panel is activated. 
37176          * @param {Roo.ContentPanel} this
37177          */
37178         "activate" : true,
37179         /**
37180          * @event deactivate
37181          * Fires when this panel is activated. 
37182          * @param {Roo.ContentPanel} this
37183          */
37184         "deactivate" : true,
37185
37186         /**
37187          * @event resize
37188          * Fires when this panel is resized if fitToFrame is true.
37189          * @param {Roo.ContentPanel} this
37190          * @param {Number} width The width after any component adjustments
37191          * @param {Number} height The height after any component adjustments
37192          */
37193         "resize" : true,
37194         
37195          /**
37196          * @event render
37197          * Fires when this tab is created
37198          * @param {Roo.ContentPanel} this
37199          */
37200         "render" : true
37201         
37202         
37203         
37204     });
37205     
37206
37207     
37208     
37209     if(this.autoScroll){
37210         this.resizeEl.setStyle("overflow", "auto");
37211     } else {
37212         // fix randome scrolling
37213         //this.el.on('scroll', function() {
37214         //    Roo.log('fix random scolling');
37215         //    this.scrollTo('top',0); 
37216         //});
37217     }
37218     content = content || this.content;
37219     if(content){
37220         this.setContent(content);
37221     }
37222     if(config && config.url){
37223         this.setUrl(this.url, this.params, this.loadOnce);
37224     }
37225     
37226     
37227     
37228     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37229     
37230     if (this.view && typeof(this.view.xtype) != 'undefined') {
37231         this.view.el = this.el.appendChild(document.createElement("div"));
37232         this.view = Roo.factory(this.view); 
37233         this.view.render  &&  this.view.render(false, '');  
37234     }
37235     
37236     
37237     this.fireEvent('render', this);
37238 };
37239
37240 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37241     
37242     tabTip : '',
37243     
37244     setRegion : function(region){
37245         this.region = region;
37246         this.setActiveClass(region && !this.background);
37247     },
37248     
37249     
37250     setActiveClass: function(state)
37251     {
37252         if(state){
37253            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37254            this.el.setStyle('position','relative');
37255         }else{
37256            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37257            this.el.setStyle('position', 'absolute');
37258         } 
37259     },
37260     
37261     /**
37262      * Returns the toolbar for this Panel if one was configured. 
37263      * @return {Roo.Toolbar} 
37264      */
37265     getToolbar : function(){
37266         return this.toolbar;
37267     },
37268     
37269     setActiveState : function(active)
37270     {
37271         this.active = active;
37272         this.setActiveClass(active);
37273         if(!active){
37274             if(this.fireEvent("deactivate", this) === false){
37275                 return false;
37276             }
37277             return true;
37278         }
37279         this.fireEvent("activate", this);
37280         return true;
37281     },
37282     /**
37283      * Updates this panel's element
37284      * @param {String} content The new content
37285      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37286     */
37287     setContent : function(content, loadScripts){
37288         this.el.update(content, loadScripts);
37289     },
37290
37291     ignoreResize : function(w, h){
37292         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37293             return true;
37294         }else{
37295             this.lastSize = {width: w, height: h};
37296             return false;
37297         }
37298     },
37299     /**
37300      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37301      * @return {Roo.UpdateManager} The UpdateManager
37302      */
37303     getUpdateManager : function(){
37304         return this.el.getUpdateManager();
37305     },
37306      /**
37307      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37308      * @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:
37309 <pre><code>
37310 panel.load({
37311     url: "your-url.php",
37312     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37313     callback: yourFunction,
37314     scope: yourObject, //(optional scope)
37315     discardUrl: false,
37316     nocache: false,
37317     text: "Loading...",
37318     timeout: 30,
37319     scripts: false
37320 });
37321 </code></pre>
37322      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37323      * 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.
37324      * @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}
37325      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37326      * @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.
37327      * @return {Roo.ContentPanel} this
37328      */
37329     load : function(){
37330         var um = this.el.getUpdateManager();
37331         um.update.apply(um, arguments);
37332         return this;
37333     },
37334
37335
37336     /**
37337      * 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.
37338      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37339      * @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)
37340      * @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)
37341      * @return {Roo.UpdateManager} The UpdateManager
37342      */
37343     setUrl : function(url, params, loadOnce){
37344         if(this.refreshDelegate){
37345             this.removeListener("activate", this.refreshDelegate);
37346         }
37347         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37348         this.on("activate", this.refreshDelegate);
37349         return this.el.getUpdateManager();
37350     },
37351     
37352     _handleRefresh : function(url, params, loadOnce){
37353         if(!loadOnce || !this.loaded){
37354             var updater = this.el.getUpdateManager();
37355             updater.update(url, params, this._setLoaded.createDelegate(this));
37356         }
37357     },
37358     
37359     _setLoaded : function(){
37360         this.loaded = true;
37361     }, 
37362     
37363     /**
37364      * Returns this panel's id
37365      * @return {String} 
37366      */
37367     getId : function(){
37368         return this.el.id;
37369     },
37370     
37371     /** 
37372      * Returns this panel's element - used by regiosn to add.
37373      * @return {Roo.Element} 
37374      */
37375     getEl : function(){
37376         return this.wrapEl || this.el;
37377     },
37378     
37379    
37380     
37381     adjustForComponents : function(width, height)
37382     {
37383         //Roo.log('adjustForComponents ');
37384         if(this.resizeEl != this.el){
37385             width -= this.el.getFrameWidth('lr');
37386             height -= this.el.getFrameWidth('tb');
37387         }
37388         if(this.toolbar){
37389             var te = this.toolbar.getEl();
37390             te.setWidth(width);
37391             height -= te.getHeight();
37392         }
37393         if(this.footer){
37394             var te = this.footer.getEl();
37395             te.setWidth(width);
37396             height -= te.getHeight();
37397         }
37398         
37399         
37400         if(this.adjustments){
37401             width += this.adjustments[0];
37402             height += this.adjustments[1];
37403         }
37404         return {"width": width, "height": height};
37405     },
37406     
37407     setSize : function(width, height){
37408         if(this.fitToFrame && !this.ignoreResize(width, height)){
37409             if(this.fitContainer && this.resizeEl != this.el){
37410                 this.el.setSize(width, height);
37411             }
37412             var size = this.adjustForComponents(width, height);
37413             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37414             this.fireEvent('resize', this, size.width, size.height);
37415         }
37416     },
37417     
37418     /**
37419      * Returns this panel's title
37420      * @return {String} 
37421      */
37422     getTitle : function(){
37423         
37424         if (typeof(this.title) != 'object') {
37425             return this.title;
37426         }
37427         
37428         var t = '';
37429         for (var k in this.title) {
37430             if (!this.title.hasOwnProperty(k)) {
37431                 continue;
37432             }
37433             
37434             if (k.indexOf('-') >= 0) {
37435                 var s = k.split('-');
37436                 for (var i = 0; i<s.length; i++) {
37437                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37438                 }
37439             } else {
37440                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37441             }
37442         }
37443         return t;
37444     },
37445     
37446     /**
37447      * Set this panel's title
37448      * @param {String} title
37449      */
37450     setTitle : function(title){
37451         this.title = title;
37452         if(this.region){
37453             this.region.updatePanelTitle(this, title);
37454         }
37455     },
37456     
37457     /**
37458      * Returns true is this panel was configured to be closable
37459      * @return {Boolean} 
37460      */
37461     isClosable : function(){
37462         return this.closable;
37463     },
37464     
37465     beforeSlide : function(){
37466         this.el.clip();
37467         this.resizeEl.clip();
37468     },
37469     
37470     afterSlide : function(){
37471         this.el.unclip();
37472         this.resizeEl.unclip();
37473     },
37474     
37475     /**
37476      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37477      *   Will fail silently if the {@link #setUrl} method has not been called.
37478      *   This does not activate the panel, just updates its content.
37479      */
37480     refresh : function(){
37481         if(this.refreshDelegate){
37482            this.loaded = false;
37483            this.refreshDelegate();
37484         }
37485     },
37486     
37487     /**
37488      * Destroys this panel
37489      */
37490     destroy : function(){
37491         this.el.removeAllListeners();
37492         var tempEl = document.createElement("span");
37493         tempEl.appendChild(this.el.dom);
37494         tempEl.innerHTML = "";
37495         this.el.remove();
37496         this.el = null;
37497     },
37498     
37499     /**
37500      * form - if the content panel contains a form - this is a reference to it.
37501      * @type {Roo.form.Form}
37502      */
37503     form : false,
37504     /**
37505      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37506      *    This contains a reference to it.
37507      * @type {Roo.View}
37508      */
37509     view : false,
37510     
37511       /**
37512      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37513      * <pre><code>
37514
37515 layout.addxtype({
37516        xtype : 'Form',
37517        items: [ .... ]
37518    }
37519 );
37520
37521 </code></pre>
37522      * @param {Object} cfg Xtype definition of item to add.
37523      */
37524     
37525     
37526     getChildContainer: function () {
37527         return this.getEl();
37528     }
37529     
37530     
37531     /*
37532         var  ret = new Roo.factory(cfg);
37533         return ret;
37534         
37535         
37536         // add form..
37537         if (cfg.xtype.match(/^Form$/)) {
37538             
37539             var el;
37540             //if (this.footer) {
37541             //    el = this.footer.container.insertSibling(false, 'before');
37542             //} else {
37543                 el = this.el.createChild();
37544             //}
37545
37546             this.form = new  Roo.form.Form(cfg);
37547             
37548             
37549             if ( this.form.allItems.length) {
37550                 this.form.render(el.dom);
37551             }
37552             return this.form;
37553         }
37554         // should only have one of theses..
37555         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37556             // views.. should not be just added - used named prop 'view''
37557             
37558             cfg.el = this.el.appendChild(document.createElement("div"));
37559             // factory?
37560             
37561             var ret = new Roo.factory(cfg);
37562              
37563              ret.render && ret.render(false, ''); // render blank..
37564             this.view = ret;
37565             return ret;
37566         }
37567         return false;
37568     }
37569     \*/
37570 });
37571  
37572 /**
37573  * @class Roo.bootstrap.panel.Grid
37574  * @extends Roo.bootstrap.panel.Content
37575  * @constructor
37576  * Create a new GridPanel.
37577  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37578  * @param {Object} config A the config object
37579   
37580  */
37581
37582
37583
37584 Roo.bootstrap.panel.Grid = function(config)
37585 {
37586     
37587       
37588     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37589         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37590
37591     config.el = this.wrapper;
37592     //this.el = this.wrapper;
37593     
37594       if (config.container) {
37595         // ctor'ed from a Border/panel.grid
37596         
37597         
37598         this.wrapper.setStyle("overflow", "hidden");
37599         this.wrapper.addClass('roo-grid-container');
37600
37601     }
37602     
37603     
37604     if(config.toolbar){
37605         var tool_el = this.wrapper.createChild();    
37606         this.toolbar = Roo.factory(config.toolbar);
37607         var ti = [];
37608         if (config.toolbar.items) {
37609             ti = config.toolbar.items ;
37610             delete config.toolbar.items ;
37611         }
37612         
37613         var nitems = [];
37614         this.toolbar.render(tool_el);
37615         for(var i =0;i < ti.length;i++) {
37616           //  Roo.log(['add child', items[i]]);
37617             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37618         }
37619         this.toolbar.items = nitems;
37620         
37621         delete config.toolbar;
37622     }
37623     
37624     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37625     config.grid.scrollBody = true;;
37626     config.grid.monitorWindowResize = false; // turn off autosizing
37627     config.grid.autoHeight = false;
37628     config.grid.autoWidth = false;
37629     
37630     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37631     
37632     if (config.background) {
37633         // render grid on panel activation (if panel background)
37634         this.on('activate', function(gp) {
37635             if (!gp.grid.rendered) {
37636                 gp.grid.render(this.wrapper);
37637                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37638             }
37639         });
37640             
37641     } else {
37642         this.grid.render(this.wrapper);
37643         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37644
37645     }
37646     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37647     // ??? needed ??? config.el = this.wrapper;
37648     
37649     
37650     
37651   
37652     // xtype created footer. - not sure if will work as we normally have to render first..
37653     if (this.footer && !this.footer.el && this.footer.xtype) {
37654         
37655         var ctr = this.grid.getView().getFooterPanel(true);
37656         this.footer.dataSource = this.grid.dataSource;
37657         this.footer = Roo.factory(this.footer, Roo);
37658         this.footer.render(ctr);
37659         
37660     }
37661     
37662     
37663     
37664     
37665      
37666 };
37667
37668 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37669     getId : function(){
37670         return this.grid.id;
37671     },
37672     
37673     /**
37674      * Returns the grid for this panel
37675      * @return {Roo.bootstrap.Table} 
37676      */
37677     getGrid : function(){
37678         return this.grid;    
37679     },
37680     
37681     setSize : function(width, height){
37682         if(!this.ignoreResize(width, height)){
37683             var grid = this.grid;
37684             var size = this.adjustForComponents(width, height);
37685             var gridel = grid.getGridEl();
37686             gridel.setSize(size.width, size.height);
37687             /*
37688             var thd = grid.getGridEl().select('thead',true).first();
37689             var tbd = grid.getGridEl().select('tbody', true).first();
37690             if (tbd) {
37691                 tbd.setSize(width, height - thd.getHeight());
37692             }
37693             */
37694             grid.autoSize();
37695         }
37696     },
37697      
37698     
37699     
37700     beforeSlide : function(){
37701         this.grid.getView().scroller.clip();
37702     },
37703     
37704     afterSlide : function(){
37705         this.grid.getView().scroller.unclip();
37706     },
37707     
37708     destroy : function(){
37709         this.grid.destroy();
37710         delete this.grid;
37711         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37712     }
37713 });
37714
37715 /**
37716  * @class Roo.bootstrap.panel.Nest
37717  * @extends Roo.bootstrap.panel.Content
37718  * @constructor
37719  * Create a new Panel, that can contain a layout.Border.
37720  * 
37721  * 
37722  * @param {Roo.BorderLayout} layout The layout for this panel
37723  * @param {String/Object} config A string to set only the title or a config object
37724  */
37725 Roo.bootstrap.panel.Nest = function(config)
37726 {
37727     // construct with only one argument..
37728     /* FIXME - implement nicer consturctors
37729     if (layout.layout) {
37730         config = layout;
37731         layout = config.layout;
37732         delete config.layout;
37733     }
37734     if (layout.xtype && !layout.getEl) {
37735         // then layout needs constructing..
37736         layout = Roo.factory(layout, Roo);
37737     }
37738     */
37739     
37740     config.el =  config.layout.getEl();
37741     
37742     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37743     
37744     config.layout.monitorWindowResize = false; // turn off autosizing
37745     this.layout = config.layout;
37746     this.layout.getEl().addClass("roo-layout-nested-layout");
37747     
37748     
37749     
37750     
37751 };
37752
37753 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37754
37755     setSize : function(width, height){
37756         if(!this.ignoreResize(width, height)){
37757             var size = this.adjustForComponents(width, height);
37758             var el = this.layout.getEl();
37759             if (size.height < 1) {
37760                 el.setWidth(size.width);   
37761             } else {
37762                 el.setSize(size.width, size.height);
37763             }
37764             var touch = el.dom.offsetWidth;
37765             this.layout.layout();
37766             // ie requires a double layout on the first pass
37767             if(Roo.isIE && !this.initialized){
37768                 this.initialized = true;
37769                 this.layout.layout();
37770             }
37771         }
37772     },
37773     
37774     // activate all subpanels if not currently active..
37775     
37776     setActiveState : function(active){
37777         this.active = active;
37778         this.setActiveClass(active);
37779         
37780         if(!active){
37781             this.fireEvent("deactivate", this);
37782             return;
37783         }
37784         
37785         this.fireEvent("activate", this);
37786         // not sure if this should happen before or after..
37787         if (!this.layout) {
37788             return; // should not happen..
37789         }
37790         var reg = false;
37791         for (var r in this.layout.regions) {
37792             reg = this.layout.getRegion(r);
37793             if (reg.getActivePanel()) {
37794                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37795                 reg.setActivePanel(reg.getActivePanel());
37796                 continue;
37797             }
37798             if (!reg.panels.length) {
37799                 continue;
37800             }
37801             reg.showPanel(reg.getPanel(0));
37802         }
37803         
37804         
37805         
37806         
37807     },
37808     
37809     /**
37810      * Returns the nested BorderLayout for this panel
37811      * @return {Roo.BorderLayout} 
37812      */
37813     getLayout : function(){
37814         return this.layout;
37815     },
37816     
37817      /**
37818      * Adds a xtype elements to the layout of the nested panel
37819      * <pre><code>
37820
37821 panel.addxtype({
37822        xtype : 'ContentPanel',
37823        region: 'west',
37824        items: [ .... ]
37825    }
37826 );
37827
37828 panel.addxtype({
37829         xtype : 'NestedLayoutPanel',
37830         region: 'west',
37831         layout: {
37832            center: { },
37833            west: { }   
37834         },
37835         items : [ ... list of content panels or nested layout panels.. ]
37836    }
37837 );
37838 </code></pre>
37839      * @param {Object} cfg Xtype definition of item to add.
37840      */
37841     addxtype : function(cfg) {
37842         return this.layout.addxtype(cfg);
37843     
37844     }
37845 });        /*
37846  * Based on:
37847  * Ext JS Library 1.1.1
37848  * Copyright(c) 2006-2007, Ext JS, LLC.
37849  *
37850  * Originally Released Under LGPL - original licence link has changed is not relivant.
37851  *
37852  * Fork - LGPL
37853  * <script type="text/javascript">
37854  */
37855 /**
37856  * @class Roo.TabPanel
37857  * @extends Roo.util.Observable
37858  * A lightweight tab container.
37859  * <br><br>
37860  * Usage:
37861  * <pre><code>
37862 // basic tabs 1, built from existing content
37863 var tabs = new Roo.TabPanel("tabs1");
37864 tabs.addTab("script", "View Script");
37865 tabs.addTab("markup", "View Markup");
37866 tabs.activate("script");
37867
37868 // more advanced tabs, built from javascript
37869 var jtabs = new Roo.TabPanel("jtabs");
37870 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37871
37872 // set up the UpdateManager
37873 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37874 var updater = tab2.getUpdateManager();
37875 updater.setDefaultUrl("ajax1.htm");
37876 tab2.on('activate', updater.refresh, updater, true);
37877
37878 // Use setUrl for Ajax loading
37879 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37880 tab3.setUrl("ajax2.htm", null, true);
37881
37882 // Disabled tab
37883 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37884 tab4.disable();
37885
37886 jtabs.activate("jtabs-1");
37887  * </code></pre>
37888  * @constructor
37889  * Create a new TabPanel.
37890  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37891  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37892  */
37893 Roo.bootstrap.panel.Tabs = function(config){
37894     /**
37895     * The container element for this TabPanel.
37896     * @type Roo.Element
37897     */
37898     this.el = Roo.get(config.el);
37899     delete config.el;
37900     if(config){
37901         if(typeof config == "boolean"){
37902             this.tabPosition = config ? "bottom" : "top";
37903         }else{
37904             Roo.apply(this, config);
37905         }
37906     }
37907     
37908     if(this.tabPosition == "bottom"){
37909         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37910         this.el.addClass("roo-tabs-bottom");
37911     }
37912     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37913     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37914     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37915     if(Roo.isIE){
37916         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37917     }
37918     if(this.tabPosition != "bottom"){
37919         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37920          * @type Roo.Element
37921          */
37922         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37923         this.el.addClass("roo-tabs-top");
37924     }
37925     this.items = [];
37926
37927     this.bodyEl.setStyle("position", "relative");
37928
37929     this.active = null;
37930     this.activateDelegate = this.activate.createDelegate(this);
37931
37932     this.addEvents({
37933         /**
37934          * @event tabchange
37935          * Fires when the active tab changes
37936          * @param {Roo.TabPanel} this
37937          * @param {Roo.TabPanelItem} activePanel The new active tab
37938          */
37939         "tabchange": true,
37940         /**
37941          * @event beforetabchange
37942          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37943          * @param {Roo.TabPanel} this
37944          * @param {Object} e Set cancel to true on this object to cancel the tab change
37945          * @param {Roo.TabPanelItem} tab The tab being changed to
37946          */
37947         "beforetabchange" : true
37948     });
37949
37950     Roo.EventManager.onWindowResize(this.onResize, this);
37951     this.cpad = this.el.getPadding("lr");
37952     this.hiddenCount = 0;
37953
37954
37955     // toolbar on the tabbar support...
37956     if (this.toolbar) {
37957         alert("no toolbar support yet");
37958         this.toolbar  = false;
37959         /*
37960         var tcfg = this.toolbar;
37961         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37962         this.toolbar = new Roo.Toolbar(tcfg);
37963         if (Roo.isSafari) {
37964             var tbl = tcfg.container.child('table', true);
37965             tbl.setAttribute('width', '100%');
37966         }
37967         */
37968         
37969     }
37970    
37971
37972
37973     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37974 };
37975
37976 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37977     /*
37978      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37979      */
37980     tabPosition : "top",
37981     /*
37982      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37983      */
37984     currentTabWidth : 0,
37985     /*
37986      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37987      */
37988     minTabWidth : 40,
37989     /*
37990      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37991      */
37992     maxTabWidth : 250,
37993     /*
37994      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37995      */
37996     preferredTabWidth : 175,
37997     /*
37998      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37999      */
38000     resizeTabs : false,
38001     /*
38002      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38003      */
38004     monitorResize : true,
38005     /*
38006      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38007      */
38008     toolbar : false,
38009
38010     /**
38011      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38012      * @param {String} id The id of the div to use <b>or create</b>
38013      * @param {String} text The text for the tab
38014      * @param {String} content (optional) Content to put in the TabPanelItem body
38015      * @param {Boolean} closable (optional) True to create a close icon on the tab
38016      * @return {Roo.TabPanelItem} The created TabPanelItem
38017      */
38018     addTab : function(id, text, content, closable, tpl)
38019     {
38020         var item = new Roo.bootstrap.panel.TabItem({
38021             panel: this,
38022             id : id,
38023             text : text,
38024             closable : closable,
38025             tpl : tpl
38026         });
38027         this.addTabItem(item);
38028         if(content){
38029             item.setContent(content);
38030         }
38031         return item;
38032     },
38033
38034     /**
38035      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38036      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38037      * @return {Roo.TabPanelItem}
38038      */
38039     getTab : function(id){
38040         return this.items[id];
38041     },
38042
38043     /**
38044      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38045      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38046      */
38047     hideTab : function(id){
38048         var t = this.items[id];
38049         if(!t.isHidden()){
38050            t.setHidden(true);
38051            this.hiddenCount++;
38052            this.autoSizeTabs();
38053         }
38054     },
38055
38056     /**
38057      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38058      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38059      */
38060     unhideTab : function(id){
38061         var t = this.items[id];
38062         if(t.isHidden()){
38063            t.setHidden(false);
38064            this.hiddenCount--;
38065            this.autoSizeTabs();
38066         }
38067     },
38068
38069     /**
38070      * Adds an existing {@link Roo.TabPanelItem}.
38071      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38072      */
38073     addTabItem : function(item){
38074         this.items[item.id] = item;
38075         this.items.push(item);
38076       //  if(this.resizeTabs){
38077     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38078   //         this.autoSizeTabs();
38079 //        }else{
38080 //            item.autoSize();
38081        // }
38082     },
38083
38084     /**
38085      * Removes a {@link Roo.TabPanelItem}.
38086      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38087      */
38088     removeTab : function(id){
38089         var items = this.items;
38090         var tab = items[id];
38091         if(!tab) { return; }
38092         var index = items.indexOf(tab);
38093         if(this.active == tab && items.length > 1){
38094             var newTab = this.getNextAvailable(index);
38095             if(newTab) {
38096                 newTab.activate();
38097             }
38098         }
38099         this.stripEl.dom.removeChild(tab.pnode.dom);
38100         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38101             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38102         }
38103         items.splice(index, 1);
38104         delete this.items[tab.id];
38105         tab.fireEvent("close", tab);
38106         tab.purgeListeners();
38107         this.autoSizeTabs();
38108     },
38109
38110     getNextAvailable : function(start){
38111         var items = this.items;
38112         var index = start;
38113         // look for a next tab that will slide over to
38114         // replace the one being removed
38115         while(index < items.length){
38116             var item = items[++index];
38117             if(item && !item.isHidden()){
38118                 return item;
38119             }
38120         }
38121         // if one isn't found select the previous tab (on the left)
38122         index = start;
38123         while(index >= 0){
38124             var item = items[--index];
38125             if(item && !item.isHidden()){
38126                 return item;
38127             }
38128         }
38129         return null;
38130     },
38131
38132     /**
38133      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38134      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38135      */
38136     disableTab : function(id){
38137         var tab = this.items[id];
38138         if(tab && this.active != tab){
38139             tab.disable();
38140         }
38141     },
38142
38143     /**
38144      * Enables a {@link Roo.TabPanelItem} that is disabled.
38145      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38146      */
38147     enableTab : function(id){
38148         var tab = this.items[id];
38149         tab.enable();
38150     },
38151
38152     /**
38153      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38154      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38155      * @return {Roo.TabPanelItem} The TabPanelItem.
38156      */
38157     activate : function(id){
38158         var tab = this.items[id];
38159         if(!tab){
38160             return null;
38161         }
38162         if(tab == this.active || tab.disabled){
38163             return tab;
38164         }
38165         var e = {};
38166         this.fireEvent("beforetabchange", this, e, tab);
38167         if(e.cancel !== true && !tab.disabled){
38168             if(this.active){
38169                 this.active.hide();
38170             }
38171             this.active = this.items[id];
38172             this.active.show();
38173             this.fireEvent("tabchange", this, this.active);
38174         }
38175         return tab;
38176     },
38177
38178     /**
38179      * Gets the active {@link Roo.TabPanelItem}.
38180      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38181      */
38182     getActiveTab : function(){
38183         return this.active;
38184     },
38185
38186     /**
38187      * Updates the tab body element to fit the height of the container element
38188      * for overflow scrolling
38189      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38190      */
38191     syncHeight : function(targetHeight){
38192         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38193         var bm = this.bodyEl.getMargins();
38194         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38195         this.bodyEl.setHeight(newHeight);
38196         return newHeight;
38197     },
38198
38199     onResize : function(){
38200         if(this.monitorResize){
38201             this.autoSizeTabs();
38202         }
38203     },
38204
38205     /**
38206      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38207      */
38208     beginUpdate : function(){
38209         this.updating = true;
38210     },
38211
38212     /**
38213      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38214      */
38215     endUpdate : function(){
38216         this.updating = false;
38217         this.autoSizeTabs();
38218     },
38219
38220     /**
38221      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38222      */
38223     autoSizeTabs : function(){
38224         var count = this.items.length;
38225         var vcount = count - this.hiddenCount;
38226         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38227             return;
38228         }
38229         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38230         var availWidth = Math.floor(w / vcount);
38231         var b = this.stripBody;
38232         if(b.getWidth() > w){
38233             var tabs = this.items;
38234             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38235             if(availWidth < this.minTabWidth){
38236                 /*if(!this.sleft){    // incomplete scrolling code
38237                     this.createScrollButtons();
38238                 }
38239                 this.showScroll();
38240                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38241             }
38242         }else{
38243             if(this.currentTabWidth < this.preferredTabWidth){
38244                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38245             }
38246         }
38247     },
38248
38249     /**
38250      * Returns the number of tabs in this TabPanel.
38251      * @return {Number}
38252      */
38253      getCount : function(){
38254          return this.items.length;
38255      },
38256
38257     /**
38258      * Resizes all the tabs to the passed width
38259      * @param {Number} The new width
38260      */
38261     setTabWidth : function(width){
38262         this.currentTabWidth = width;
38263         for(var i = 0, len = this.items.length; i < len; i++) {
38264                 if(!this.items[i].isHidden()) {
38265                 this.items[i].setWidth(width);
38266             }
38267         }
38268     },
38269
38270     /**
38271      * Destroys this TabPanel
38272      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38273      */
38274     destroy : function(removeEl){
38275         Roo.EventManager.removeResizeListener(this.onResize, this);
38276         for(var i = 0, len = this.items.length; i < len; i++){
38277             this.items[i].purgeListeners();
38278         }
38279         if(removeEl === true){
38280             this.el.update("");
38281             this.el.remove();
38282         }
38283     },
38284     
38285     createStrip : function(container)
38286     {
38287         var strip = document.createElement("nav");
38288         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38289         container.appendChild(strip);
38290         return strip;
38291     },
38292     
38293     createStripList : function(strip)
38294     {
38295         // div wrapper for retard IE
38296         // returns the "tr" element.
38297         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38298         //'<div class="x-tabs-strip-wrap">'+
38299           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38300           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38301         return strip.firstChild; //.firstChild.firstChild.firstChild;
38302     },
38303     createBody : function(container)
38304     {
38305         var body = document.createElement("div");
38306         Roo.id(body, "tab-body");
38307         //Roo.fly(body).addClass("x-tabs-body");
38308         Roo.fly(body).addClass("tab-content");
38309         container.appendChild(body);
38310         return body;
38311     },
38312     createItemBody :function(bodyEl, id){
38313         var body = Roo.getDom(id);
38314         if(!body){
38315             body = document.createElement("div");
38316             body.id = id;
38317         }
38318         //Roo.fly(body).addClass("x-tabs-item-body");
38319         Roo.fly(body).addClass("tab-pane");
38320          bodyEl.insertBefore(body, bodyEl.firstChild);
38321         return body;
38322     },
38323     /** @private */
38324     createStripElements :  function(stripEl, text, closable, tpl)
38325     {
38326         var td = document.createElement("li"); // was td..
38327         
38328         
38329         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38330         
38331         
38332         stripEl.appendChild(td);
38333         /*if(closable){
38334             td.className = "x-tabs-closable";
38335             if(!this.closeTpl){
38336                 this.closeTpl = new Roo.Template(
38337                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38338                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38339                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38340                 );
38341             }
38342             var el = this.closeTpl.overwrite(td, {"text": text});
38343             var close = el.getElementsByTagName("div")[0];
38344             var inner = el.getElementsByTagName("em")[0];
38345             return {"el": el, "close": close, "inner": inner};
38346         } else {
38347         */
38348         // not sure what this is..
38349 //            if(!this.tabTpl){
38350                 //this.tabTpl = new Roo.Template(
38351                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38352                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38353                 //);
38354 //                this.tabTpl = new Roo.Template(
38355 //                   '<a href="#">' +
38356 //                   '<span unselectable="on"' +
38357 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38358 //                            ' >{text}</span></a>'
38359 //                );
38360 //                
38361 //            }
38362
38363
38364             var template = tpl || this.tabTpl || false;
38365             
38366             if(!template){
38367                 
38368                 template = new Roo.Template(
38369                    '<a href="#">' +
38370                    '<span unselectable="on"' +
38371                             (this.disableTooltips ? '' : ' title="{text}"') +
38372                             ' >{text}</span></a>'
38373                 );
38374             }
38375             
38376             switch (typeof(template)) {
38377                 case 'object' :
38378                     break;
38379                 case 'string' :
38380                     template = new Roo.Template(template);
38381                     break;
38382                 default :
38383                     break;
38384             }
38385             
38386             var el = template.overwrite(td, {"text": text});
38387             
38388             var inner = el.getElementsByTagName("span")[0];
38389             
38390             return {"el": el, "inner": inner};
38391             
38392     }
38393         
38394     
38395 });
38396
38397 /**
38398  * @class Roo.TabPanelItem
38399  * @extends Roo.util.Observable
38400  * Represents an individual item (tab plus body) in a TabPanel.
38401  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38402  * @param {String} id The id of this TabPanelItem
38403  * @param {String} text The text for the tab of this TabPanelItem
38404  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38405  */
38406 Roo.bootstrap.panel.TabItem = function(config){
38407     /**
38408      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38409      * @type Roo.TabPanel
38410      */
38411     this.tabPanel = config.panel;
38412     /**
38413      * The id for this TabPanelItem
38414      * @type String
38415      */
38416     this.id = config.id;
38417     /** @private */
38418     this.disabled = false;
38419     /** @private */
38420     this.text = config.text;
38421     /** @private */
38422     this.loaded = false;
38423     this.closable = config.closable;
38424
38425     /**
38426      * The body element for this TabPanelItem.
38427      * @type Roo.Element
38428      */
38429     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38430     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38431     this.bodyEl.setStyle("display", "block");
38432     this.bodyEl.setStyle("zoom", "1");
38433     //this.hideAction();
38434
38435     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38436     /** @private */
38437     this.el = Roo.get(els.el);
38438     this.inner = Roo.get(els.inner, true);
38439     this.textEl = Roo.get(this.el.dom.firstChild, true);
38440     this.pnode = Roo.get(els.el.parentNode, true);
38441 //    this.el.on("mousedown", this.onTabMouseDown, this);
38442     this.el.on("click", this.onTabClick, this);
38443     /** @private */
38444     if(config.closable){
38445         var c = Roo.get(els.close, true);
38446         c.dom.title = this.closeText;
38447         c.addClassOnOver("close-over");
38448         c.on("click", this.closeClick, this);
38449      }
38450
38451     this.addEvents({
38452          /**
38453          * @event activate
38454          * Fires when this tab becomes the active tab.
38455          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38456          * @param {Roo.TabPanelItem} this
38457          */
38458         "activate": true,
38459         /**
38460          * @event beforeclose
38461          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38462          * @param {Roo.TabPanelItem} this
38463          * @param {Object} e Set cancel to true on this object to cancel the close.
38464          */
38465         "beforeclose": true,
38466         /**
38467          * @event close
38468          * Fires when this tab is closed.
38469          * @param {Roo.TabPanelItem} this
38470          */
38471          "close": true,
38472         /**
38473          * @event deactivate
38474          * Fires when this tab is no longer the active tab.
38475          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38476          * @param {Roo.TabPanelItem} this
38477          */
38478          "deactivate" : true
38479     });
38480     this.hidden = false;
38481
38482     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38483 };
38484
38485 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38486            {
38487     purgeListeners : function(){
38488        Roo.util.Observable.prototype.purgeListeners.call(this);
38489        this.el.removeAllListeners();
38490     },
38491     /**
38492      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38493      */
38494     show : function(){
38495         this.pnode.addClass("active");
38496         this.showAction();
38497         if(Roo.isOpera){
38498             this.tabPanel.stripWrap.repaint();
38499         }
38500         this.fireEvent("activate", this.tabPanel, this);
38501     },
38502
38503     /**
38504      * Returns true if this tab is the active tab.
38505      * @return {Boolean}
38506      */
38507     isActive : function(){
38508         return this.tabPanel.getActiveTab() == this;
38509     },
38510
38511     /**
38512      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38513      */
38514     hide : function(){
38515         this.pnode.removeClass("active");
38516         this.hideAction();
38517         this.fireEvent("deactivate", this.tabPanel, this);
38518     },
38519
38520     hideAction : function(){
38521         this.bodyEl.hide();
38522         this.bodyEl.setStyle("position", "absolute");
38523         this.bodyEl.setLeft("-20000px");
38524         this.bodyEl.setTop("-20000px");
38525     },
38526
38527     showAction : function(){
38528         this.bodyEl.setStyle("position", "relative");
38529         this.bodyEl.setTop("");
38530         this.bodyEl.setLeft("");
38531         this.bodyEl.show();
38532     },
38533
38534     /**
38535      * Set the tooltip for the tab.
38536      * @param {String} tooltip The tab's tooltip
38537      */
38538     setTooltip : function(text){
38539         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38540             this.textEl.dom.qtip = text;
38541             this.textEl.dom.removeAttribute('title');
38542         }else{
38543             this.textEl.dom.title = text;
38544         }
38545     },
38546
38547     onTabClick : function(e){
38548         e.preventDefault();
38549         this.tabPanel.activate(this.id);
38550     },
38551
38552     onTabMouseDown : function(e){
38553         e.preventDefault();
38554         this.tabPanel.activate(this.id);
38555     },
38556 /*
38557     getWidth : function(){
38558         return this.inner.getWidth();
38559     },
38560
38561     setWidth : function(width){
38562         var iwidth = width - this.pnode.getPadding("lr");
38563         this.inner.setWidth(iwidth);
38564         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38565         this.pnode.setWidth(width);
38566     },
38567 */
38568     /**
38569      * Show or hide the tab
38570      * @param {Boolean} hidden True to hide or false to show.
38571      */
38572     setHidden : function(hidden){
38573         this.hidden = hidden;
38574         this.pnode.setStyle("display", hidden ? "none" : "");
38575     },
38576
38577     /**
38578      * Returns true if this tab is "hidden"
38579      * @return {Boolean}
38580      */
38581     isHidden : function(){
38582         return this.hidden;
38583     },
38584
38585     /**
38586      * Returns the text for this tab
38587      * @return {String}
38588      */
38589     getText : function(){
38590         return this.text;
38591     },
38592     /*
38593     autoSize : function(){
38594         //this.el.beginMeasure();
38595         this.textEl.setWidth(1);
38596         /*
38597          *  #2804 [new] Tabs in Roojs
38598          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38599          */
38600         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38601         //this.el.endMeasure();
38602     //},
38603
38604     /**
38605      * Sets the text for the tab (Note: this also sets the tooltip text)
38606      * @param {String} text The tab's text and tooltip
38607      */
38608     setText : function(text){
38609         this.text = text;
38610         this.textEl.update(text);
38611         this.setTooltip(text);
38612         //if(!this.tabPanel.resizeTabs){
38613         //    this.autoSize();
38614         //}
38615     },
38616     /**
38617      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38618      */
38619     activate : function(){
38620         this.tabPanel.activate(this.id);
38621     },
38622
38623     /**
38624      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38625      */
38626     disable : function(){
38627         if(this.tabPanel.active != this){
38628             this.disabled = true;
38629             this.pnode.addClass("disabled");
38630         }
38631     },
38632
38633     /**
38634      * Enables this TabPanelItem if it was previously disabled.
38635      */
38636     enable : function(){
38637         this.disabled = false;
38638         this.pnode.removeClass("disabled");
38639     },
38640
38641     /**
38642      * Sets the content for this TabPanelItem.
38643      * @param {String} content The content
38644      * @param {Boolean} loadScripts true to look for and load scripts
38645      */
38646     setContent : function(content, loadScripts){
38647         this.bodyEl.update(content, loadScripts);
38648     },
38649
38650     /**
38651      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38652      * @return {Roo.UpdateManager} The UpdateManager
38653      */
38654     getUpdateManager : function(){
38655         return this.bodyEl.getUpdateManager();
38656     },
38657
38658     /**
38659      * Set a URL to be used to load the content for this TabPanelItem.
38660      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38661      * @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)
38662      * @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)
38663      * @return {Roo.UpdateManager} The UpdateManager
38664      */
38665     setUrl : function(url, params, loadOnce){
38666         if(this.refreshDelegate){
38667             this.un('activate', this.refreshDelegate);
38668         }
38669         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38670         this.on("activate", this.refreshDelegate);
38671         return this.bodyEl.getUpdateManager();
38672     },
38673
38674     /** @private */
38675     _handleRefresh : function(url, params, loadOnce){
38676         if(!loadOnce || !this.loaded){
38677             var updater = this.bodyEl.getUpdateManager();
38678             updater.update(url, params, this._setLoaded.createDelegate(this));
38679         }
38680     },
38681
38682     /**
38683      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38684      *   Will fail silently if the setUrl method has not been called.
38685      *   This does not activate the panel, just updates its content.
38686      */
38687     refresh : function(){
38688         if(this.refreshDelegate){
38689            this.loaded = false;
38690            this.refreshDelegate();
38691         }
38692     },
38693
38694     /** @private */
38695     _setLoaded : function(){
38696         this.loaded = true;
38697     },
38698
38699     /** @private */
38700     closeClick : function(e){
38701         var o = {};
38702         e.stopEvent();
38703         this.fireEvent("beforeclose", this, o);
38704         if(o.cancel !== true){
38705             this.tabPanel.removeTab(this.id);
38706         }
38707     },
38708     /**
38709      * The text displayed in the tooltip for the close icon.
38710      * @type String
38711      */
38712     closeText : "Close this tab"
38713 });
38714 /**
38715 *    This script refer to:
38716 *    Title: International Telephone Input
38717 *    Author: Jack O'Connor
38718 *    Code version:  v12.1.12
38719 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38720 **/
38721
38722 Roo.bootstrap.PhoneInputData = function() {
38723     var d = [
38724       [
38725         "Afghanistan (‫افغانستان‬‎)",
38726         "af",
38727         "93"
38728       ],
38729       [
38730         "Albania (Shqipëri)",
38731         "al",
38732         "355"
38733       ],
38734       [
38735         "Algeria (‫الجزائر‬‎)",
38736         "dz",
38737         "213"
38738       ],
38739       [
38740         "American Samoa",
38741         "as",
38742         "1684"
38743       ],
38744       [
38745         "Andorra",
38746         "ad",
38747         "376"
38748       ],
38749       [
38750         "Angola",
38751         "ao",
38752         "244"
38753       ],
38754       [
38755         "Anguilla",
38756         "ai",
38757         "1264"
38758       ],
38759       [
38760         "Antigua and Barbuda",
38761         "ag",
38762         "1268"
38763       ],
38764       [
38765         "Argentina",
38766         "ar",
38767         "54"
38768       ],
38769       [
38770         "Armenia (Հայաստան)",
38771         "am",
38772         "374"
38773       ],
38774       [
38775         "Aruba",
38776         "aw",
38777         "297"
38778       ],
38779       [
38780         "Australia",
38781         "au",
38782         "61",
38783         0
38784       ],
38785       [
38786         "Austria (Österreich)",
38787         "at",
38788         "43"
38789       ],
38790       [
38791         "Azerbaijan (Azərbaycan)",
38792         "az",
38793         "994"
38794       ],
38795       [
38796         "Bahamas",
38797         "bs",
38798         "1242"
38799       ],
38800       [
38801         "Bahrain (‫البحرين‬‎)",
38802         "bh",
38803         "973"
38804       ],
38805       [
38806         "Bangladesh (বাংলাদেশ)",
38807         "bd",
38808         "880"
38809       ],
38810       [
38811         "Barbados",
38812         "bb",
38813         "1246"
38814       ],
38815       [
38816         "Belarus (Беларусь)",
38817         "by",
38818         "375"
38819       ],
38820       [
38821         "Belgium (België)",
38822         "be",
38823         "32"
38824       ],
38825       [
38826         "Belize",
38827         "bz",
38828         "501"
38829       ],
38830       [
38831         "Benin (Bénin)",
38832         "bj",
38833         "229"
38834       ],
38835       [
38836         "Bermuda",
38837         "bm",
38838         "1441"
38839       ],
38840       [
38841         "Bhutan (འབྲུག)",
38842         "bt",
38843         "975"
38844       ],
38845       [
38846         "Bolivia",
38847         "bo",
38848         "591"
38849       ],
38850       [
38851         "Bosnia and Herzegovina (Босна и Херцеговина)",
38852         "ba",
38853         "387"
38854       ],
38855       [
38856         "Botswana",
38857         "bw",
38858         "267"
38859       ],
38860       [
38861         "Brazil (Brasil)",
38862         "br",
38863         "55"
38864       ],
38865       [
38866         "British Indian Ocean Territory",
38867         "io",
38868         "246"
38869       ],
38870       [
38871         "British Virgin Islands",
38872         "vg",
38873         "1284"
38874       ],
38875       [
38876         "Brunei",
38877         "bn",
38878         "673"
38879       ],
38880       [
38881         "Bulgaria (България)",
38882         "bg",
38883         "359"
38884       ],
38885       [
38886         "Burkina Faso",
38887         "bf",
38888         "226"
38889       ],
38890       [
38891         "Burundi (Uburundi)",
38892         "bi",
38893         "257"
38894       ],
38895       [
38896         "Cambodia (កម្ពុជា)",
38897         "kh",
38898         "855"
38899       ],
38900       [
38901         "Cameroon (Cameroun)",
38902         "cm",
38903         "237"
38904       ],
38905       [
38906         "Canada",
38907         "ca",
38908         "1",
38909         1,
38910         ["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"]
38911       ],
38912       [
38913         "Cape Verde (Kabu Verdi)",
38914         "cv",
38915         "238"
38916       ],
38917       [
38918         "Caribbean Netherlands",
38919         "bq",
38920         "599",
38921         1
38922       ],
38923       [
38924         "Cayman Islands",
38925         "ky",
38926         "1345"
38927       ],
38928       [
38929         "Central African Republic (République centrafricaine)",
38930         "cf",
38931         "236"
38932       ],
38933       [
38934         "Chad (Tchad)",
38935         "td",
38936         "235"
38937       ],
38938       [
38939         "Chile",
38940         "cl",
38941         "56"
38942       ],
38943       [
38944         "China (中国)",
38945         "cn",
38946         "86"
38947       ],
38948       [
38949         "Christmas Island",
38950         "cx",
38951         "61",
38952         2
38953       ],
38954       [
38955         "Cocos (Keeling) Islands",
38956         "cc",
38957         "61",
38958         1
38959       ],
38960       [
38961         "Colombia",
38962         "co",
38963         "57"
38964       ],
38965       [
38966         "Comoros (‫جزر القمر‬‎)",
38967         "km",
38968         "269"
38969       ],
38970       [
38971         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38972         "cd",
38973         "243"
38974       ],
38975       [
38976         "Congo (Republic) (Congo-Brazzaville)",
38977         "cg",
38978         "242"
38979       ],
38980       [
38981         "Cook Islands",
38982         "ck",
38983         "682"
38984       ],
38985       [
38986         "Costa Rica",
38987         "cr",
38988         "506"
38989       ],
38990       [
38991         "Côte d’Ivoire",
38992         "ci",
38993         "225"
38994       ],
38995       [
38996         "Croatia (Hrvatska)",
38997         "hr",
38998         "385"
38999       ],
39000       [
39001         "Cuba",
39002         "cu",
39003         "53"
39004       ],
39005       [
39006         "Curaçao",
39007         "cw",
39008         "599",
39009         0
39010       ],
39011       [
39012         "Cyprus (Κύπρος)",
39013         "cy",
39014         "357"
39015       ],
39016       [
39017         "Czech Republic (Česká republika)",
39018         "cz",
39019         "420"
39020       ],
39021       [
39022         "Denmark (Danmark)",
39023         "dk",
39024         "45"
39025       ],
39026       [
39027         "Djibouti",
39028         "dj",
39029         "253"
39030       ],
39031       [
39032         "Dominica",
39033         "dm",
39034         "1767"
39035       ],
39036       [
39037         "Dominican Republic (República Dominicana)",
39038         "do",
39039         "1",
39040         2,
39041         ["809", "829", "849"]
39042       ],
39043       [
39044         "Ecuador",
39045         "ec",
39046         "593"
39047       ],
39048       [
39049         "Egypt (‫مصر‬‎)",
39050         "eg",
39051         "20"
39052       ],
39053       [
39054         "El Salvador",
39055         "sv",
39056         "503"
39057       ],
39058       [
39059         "Equatorial Guinea (Guinea Ecuatorial)",
39060         "gq",
39061         "240"
39062       ],
39063       [
39064         "Eritrea",
39065         "er",
39066         "291"
39067       ],
39068       [
39069         "Estonia (Eesti)",
39070         "ee",
39071         "372"
39072       ],
39073       [
39074         "Ethiopia",
39075         "et",
39076         "251"
39077       ],
39078       [
39079         "Falkland Islands (Islas Malvinas)",
39080         "fk",
39081         "500"
39082       ],
39083       [
39084         "Faroe Islands (Føroyar)",
39085         "fo",
39086         "298"
39087       ],
39088       [
39089         "Fiji",
39090         "fj",
39091         "679"
39092       ],
39093       [
39094         "Finland (Suomi)",
39095         "fi",
39096         "358",
39097         0
39098       ],
39099       [
39100         "France",
39101         "fr",
39102         "33"
39103       ],
39104       [
39105         "French Guiana (Guyane française)",
39106         "gf",
39107         "594"
39108       ],
39109       [
39110         "French Polynesia (Polynésie française)",
39111         "pf",
39112         "689"
39113       ],
39114       [
39115         "Gabon",
39116         "ga",
39117         "241"
39118       ],
39119       [
39120         "Gambia",
39121         "gm",
39122         "220"
39123       ],
39124       [
39125         "Georgia (საქართველო)",
39126         "ge",
39127         "995"
39128       ],
39129       [
39130         "Germany (Deutschland)",
39131         "de",
39132         "49"
39133       ],
39134       [
39135         "Ghana (Gaana)",
39136         "gh",
39137         "233"
39138       ],
39139       [
39140         "Gibraltar",
39141         "gi",
39142         "350"
39143       ],
39144       [
39145         "Greece (Ελλάδα)",
39146         "gr",
39147         "30"
39148       ],
39149       [
39150         "Greenland (Kalaallit Nunaat)",
39151         "gl",
39152         "299"
39153       ],
39154       [
39155         "Grenada",
39156         "gd",
39157         "1473"
39158       ],
39159       [
39160         "Guadeloupe",
39161         "gp",
39162         "590",
39163         0
39164       ],
39165       [
39166         "Guam",
39167         "gu",
39168         "1671"
39169       ],
39170       [
39171         "Guatemala",
39172         "gt",
39173         "502"
39174       ],
39175       [
39176         "Guernsey",
39177         "gg",
39178         "44",
39179         1
39180       ],
39181       [
39182         "Guinea (Guinée)",
39183         "gn",
39184         "224"
39185       ],
39186       [
39187         "Guinea-Bissau (Guiné Bissau)",
39188         "gw",
39189         "245"
39190       ],
39191       [
39192         "Guyana",
39193         "gy",
39194         "592"
39195       ],
39196       [
39197         "Haiti",
39198         "ht",
39199         "509"
39200       ],
39201       [
39202         "Honduras",
39203         "hn",
39204         "504"
39205       ],
39206       [
39207         "Hong Kong (香港)",
39208         "hk",
39209         "852"
39210       ],
39211       [
39212         "Hungary (Magyarország)",
39213         "hu",
39214         "36"
39215       ],
39216       [
39217         "Iceland (Ísland)",
39218         "is",
39219         "354"
39220       ],
39221       [
39222         "India (भारत)",
39223         "in",
39224         "91"
39225       ],
39226       [
39227         "Indonesia",
39228         "id",
39229         "62"
39230       ],
39231       [
39232         "Iran (‫ایران‬‎)",
39233         "ir",
39234         "98"
39235       ],
39236       [
39237         "Iraq (‫العراق‬‎)",
39238         "iq",
39239         "964"
39240       ],
39241       [
39242         "Ireland",
39243         "ie",
39244         "353"
39245       ],
39246       [
39247         "Isle of Man",
39248         "im",
39249         "44",
39250         2
39251       ],
39252       [
39253         "Israel (‫ישראל‬‎)",
39254         "il",
39255         "972"
39256       ],
39257       [
39258         "Italy (Italia)",
39259         "it",
39260         "39",
39261         0
39262       ],
39263       [
39264         "Jamaica",
39265         "jm",
39266         "1876"
39267       ],
39268       [
39269         "Japan (日本)",
39270         "jp",
39271         "81"
39272       ],
39273       [
39274         "Jersey",
39275         "je",
39276         "44",
39277         3
39278       ],
39279       [
39280         "Jordan (‫الأردن‬‎)",
39281         "jo",
39282         "962"
39283       ],
39284       [
39285         "Kazakhstan (Казахстан)",
39286         "kz",
39287         "7",
39288         1
39289       ],
39290       [
39291         "Kenya",
39292         "ke",
39293         "254"
39294       ],
39295       [
39296         "Kiribati",
39297         "ki",
39298         "686"
39299       ],
39300       [
39301         "Kosovo",
39302         "xk",
39303         "383"
39304       ],
39305       [
39306         "Kuwait (‫الكويت‬‎)",
39307         "kw",
39308         "965"
39309       ],
39310       [
39311         "Kyrgyzstan (Кыргызстан)",
39312         "kg",
39313         "996"
39314       ],
39315       [
39316         "Laos (ລາວ)",
39317         "la",
39318         "856"
39319       ],
39320       [
39321         "Latvia (Latvija)",
39322         "lv",
39323         "371"
39324       ],
39325       [
39326         "Lebanon (‫لبنان‬‎)",
39327         "lb",
39328         "961"
39329       ],
39330       [
39331         "Lesotho",
39332         "ls",
39333         "266"
39334       ],
39335       [
39336         "Liberia",
39337         "lr",
39338         "231"
39339       ],
39340       [
39341         "Libya (‫ليبيا‬‎)",
39342         "ly",
39343         "218"
39344       ],
39345       [
39346         "Liechtenstein",
39347         "li",
39348         "423"
39349       ],
39350       [
39351         "Lithuania (Lietuva)",
39352         "lt",
39353         "370"
39354       ],
39355       [
39356         "Luxembourg",
39357         "lu",
39358         "352"
39359       ],
39360       [
39361         "Macau (澳門)",
39362         "mo",
39363         "853"
39364       ],
39365       [
39366         "Macedonia (FYROM) (Македонија)",
39367         "mk",
39368         "389"
39369       ],
39370       [
39371         "Madagascar (Madagasikara)",
39372         "mg",
39373         "261"
39374       ],
39375       [
39376         "Malawi",
39377         "mw",
39378         "265"
39379       ],
39380       [
39381         "Malaysia",
39382         "my",
39383         "60"
39384       ],
39385       [
39386         "Maldives",
39387         "mv",
39388         "960"
39389       ],
39390       [
39391         "Mali",
39392         "ml",
39393         "223"
39394       ],
39395       [
39396         "Malta",
39397         "mt",
39398         "356"
39399       ],
39400       [
39401         "Marshall Islands",
39402         "mh",
39403         "692"
39404       ],
39405       [
39406         "Martinique",
39407         "mq",
39408         "596"
39409       ],
39410       [
39411         "Mauritania (‫موريتانيا‬‎)",
39412         "mr",
39413         "222"
39414       ],
39415       [
39416         "Mauritius (Moris)",
39417         "mu",
39418         "230"
39419       ],
39420       [
39421         "Mayotte",
39422         "yt",
39423         "262",
39424         1
39425       ],
39426       [
39427         "Mexico (México)",
39428         "mx",
39429         "52"
39430       ],
39431       [
39432         "Micronesia",
39433         "fm",
39434         "691"
39435       ],
39436       [
39437         "Moldova (Republica Moldova)",
39438         "md",
39439         "373"
39440       ],
39441       [
39442         "Monaco",
39443         "mc",
39444         "377"
39445       ],
39446       [
39447         "Mongolia (Монгол)",
39448         "mn",
39449         "976"
39450       ],
39451       [
39452         "Montenegro (Crna Gora)",
39453         "me",
39454         "382"
39455       ],
39456       [
39457         "Montserrat",
39458         "ms",
39459         "1664"
39460       ],
39461       [
39462         "Morocco (‫المغرب‬‎)",
39463         "ma",
39464         "212",
39465         0
39466       ],
39467       [
39468         "Mozambique (Moçambique)",
39469         "mz",
39470         "258"
39471       ],
39472       [
39473         "Myanmar (Burma) (မြန်မာ)",
39474         "mm",
39475         "95"
39476       ],
39477       [
39478         "Namibia (Namibië)",
39479         "na",
39480         "264"
39481       ],
39482       [
39483         "Nauru",
39484         "nr",
39485         "674"
39486       ],
39487       [
39488         "Nepal (नेपाल)",
39489         "np",
39490         "977"
39491       ],
39492       [
39493         "Netherlands (Nederland)",
39494         "nl",
39495         "31"
39496       ],
39497       [
39498         "New Caledonia (Nouvelle-Calédonie)",
39499         "nc",
39500         "687"
39501       ],
39502       [
39503         "New Zealand",
39504         "nz",
39505         "64"
39506       ],
39507       [
39508         "Nicaragua",
39509         "ni",
39510         "505"
39511       ],
39512       [
39513         "Niger (Nijar)",
39514         "ne",
39515         "227"
39516       ],
39517       [
39518         "Nigeria",
39519         "ng",
39520         "234"
39521       ],
39522       [
39523         "Niue",
39524         "nu",
39525         "683"
39526       ],
39527       [
39528         "Norfolk Island",
39529         "nf",
39530         "672"
39531       ],
39532       [
39533         "North Korea (조선 민주주의 인민 공화국)",
39534         "kp",
39535         "850"
39536       ],
39537       [
39538         "Northern Mariana Islands",
39539         "mp",
39540         "1670"
39541       ],
39542       [
39543         "Norway (Norge)",
39544         "no",
39545         "47",
39546         0
39547       ],
39548       [
39549         "Oman (‫عُمان‬‎)",
39550         "om",
39551         "968"
39552       ],
39553       [
39554         "Pakistan (‫پاکستان‬‎)",
39555         "pk",
39556         "92"
39557       ],
39558       [
39559         "Palau",
39560         "pw",
39561         "680"
39562       ],
39563       [
39564         "Palestine (‫فلسطين‬‎)",
39565         "ps",
39566         "970"
39567       ],
39568       [
39569         "Panama (Panamá)",
39570         "pa",
39571         "507"
39572       ],
39573       [
39574         "Papua New Guinea",
39575         "pg",
39576         "675"
39577       ],
39578       [
39579         "Paraguay",
39580         "py",
39581         "595"
39582       ],
39583       [
39584         "Peru (Perú)",
39585         "pe",
39586         "51"
39587       ],
39588       [
39589         "Philippines",
39590         "ph",
39591         "63"
39592       ],
39593       [
39594         "Poland (Polska)",
39595         "pl",
39596         "48"
39597       ],
39598       [
39599         "Portugal",
39600         "pt",
39601         "351"
39602       ],
39603       [
39604         "Puerto Rico",
39605         "pr",
39606         "1",
39607         3,
39608         ["787", "939"]
39609       ],
39610       [
39611         "Qatar (‫قطر‬‎)",
39612         "qa",
39613         "974"
39614       ],
39615       [
39616         "Réunion (La Réunion)",
39617         "re",
39618         "262",
39619         0
39620       ],
39621       [
39622         "Romania (România)",
39623         "ro",
39624         "40"
39625       ],
39626       [
39627         "Russia (Россия)",
39628         "ru",
39629         "7",
39630         0
39631       ],
39632       [
39633         "Rwanda",
39634         "rw",
39635         "250"
39636       ],
39637       [
39638         "Saint Barthélemy",
39639         "bl",
39640         "590",
39641         1
39642       ],
39643       [
39644         "Saint Helena",
39645         "sh",
39646         "290"
39647       ],
39648       [
39649         "Saint Kitts and Nevis",
39650         "kn",
39651         "1869"
39652       ],
39653       [
39654         "Saint Lucia",
39655         "lc",
39656         "1758"
39657       ],
39658       [
39659         "Saint Martin (Saint-Martin (partie française))",
39660         "mf",
39661         "590",
39662         2
39663       ],
39664       [
39665         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39666         "pm",
39667         "508"
39668       ],
39669       [
39670         "Saint Vincent and the Grenadines",
39671         "vc",
39672         "1784"
39673       ],
39674       [
39675         "Samoa",
39676         "ws",
39677         "685"
39678       ],
39679       [
39680         "San Marino",
39681         "sm",
39682         "378"
39683       ],
39684       [
39685         "São Tomé and Príncipe (São Tomé e Príncipe)",
39686         "st",
39687         "239"
39688       ],
39689       [
39690         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39691         "sa",
39692         "966"
39693       ],
39694       [
39695         "Senegal (Sénégal)",
39696         "sn",
39697         "221"
39698       ],
39699       [
39700         "Serbia (Србија)",
39701         "rs",
39702         "381"
39703       ],
39704       [
39705         "Seychelles",
39706         "sc",
39707         "248"
39708       ],
39709       [
39710         "Sierra Leone",
39711         "sl",
39712         "232"
39713       ],
39714       [
39715         "Singapore",
39716         "sg",
39717         "65"
39718       ],
39719       [
39720         "Sint Maarten",
39721         "sx",
39722         "1721"
39723       ],
39724       [
39725         "Slovakia (Slovensko)",
39726         "sk",
39727         "421"
39728       ],
39729       [
39730         "Slovenia (Slovenija)",
39731         "si",
39732         "386"
39733       ],
39734       [
39735         "Solomon Islands",
39736         "sb",
39737         "677"
39738       ],
39739       [
39740         "Somalia (Soomaaliya)",
39741         "so",
39742         "252"
39743       ],
39744       [
39745         "South Africa",
39746         "za",
39747         "27"
39748       ],
39749       [
39750         "South Korea (대한민국)",
39751         "kr",
39752         "82"
39753       ],
39754       [
39755         "South Sudan (‫جنوب السودان‬‎)",
39756         "ss",
39757         "211"
39758       ],
39759       [
39760         "Spain (España)",
39761         "es",
39762         "34"
39763       ],
39764       [
39765         "Sri Lanka (ශ්‍රී ලංකාව)",
39766         "lk",
39767         "94"
39768       ],
39769       [
39770         "Sudan (‫السودان‬‎)",
39771         "sd",
39772         "249"
39773       ],
39774       [
39775         "Suriname",
39776         "sr",
39777         "597"
39778       ],
39779       [
39780         "Svalbard and Jan Mayen",
39781         "sj",
39782         "47",
39783         1
39784       ],
39785       [
39786         "Swaziland",
39787         "sz",
39788         "268"
39789       ],
39790       [
39791         "Sweden (Sverige)",
39792         "se",
39793         "46"
39794       ],
39795       [
39796         "Switzerland (Schweiz)",
39797         "ch",
39798         "41"
39799       ],
39800       [
39801         "Syria (‫سوريا‬‎)",
39802         "sy",
39803         "963"
39804       ],
39805       [
39806         "Taiwan (台灣)",
39807         "tw",
39808         "886"
39809       ],
39810       [
39811         "Tajikistan",
39812         "tj",
39813         "992"
39814       ],
39815       [
39816         "Tanzania",
39817         "tz",
39818         "255"
39819       ],
39820       [
39821         "Thailand (ไทย)",
39822         "th",
39823         "66"
39824       ],
39825       [
39826         "Timor-Leste",
39827         "tl",
39828         "670"
39829       ],
39830       [
39831         "Togo",
39832         "tg",
39833         "228"
39834       ],
39835       [
39836         "Tokelau",
39837         "tk",
39838         "690"
39839       ],
39840       [
39841         "Tonga",
39842         "to",
39843         "676"
39844       ],
39845       [
39846         "Trinidad and Tobago",
39847         "tt",
39848         "1868"
39849       ],
39850       [
39851         "Tunisia (‫تونس‬‎)",
39852         "tn",
39853         "216"
39854       ],
39855       [
39856         "Turkey (Türkiye)",
39857         "tr",
39858         "90"
39859       ],
39860       [
39861         "Turkmenistan",
39862         "tm",
39863         "993"
39864       ],
39865       [
39866         "Turks and Caicos Islands",
39867         "tc",
39868         "1649"
39869       ],
39870       [
39871         "Tuvalu",
39872         "tv",
39873         "688"
39874       ],
39875       [
39876         "U.S. Virgin Islands",
39877         "vi",
39878         "1340"
39879       ],
39880       [
39881         "Uganda",
39882         "ug",
39883         "256"
39884       ],
39885       [
39886         "Ukraine (Україна)",
39887         "ua",
39888         "380"
39889       ],
39890       [
39891         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39892         "ae",
39893         "971"
39894       ],
39895       [
39896         "United Kingdom",
39897         "gb",
39898         "44",
39899         0
39900       ],
39901       [
39902         "United States",
39903         "us",
39904         "1",
39905         0
39906       ],
39907       [
39908         "Uruguay",
39909         "uy",
39910         "598"
39911       ],
39912       [
39913         "Uzbekistan (Oʻzbekiston)",
39914         "uz",
39915         "998"
39916       ],
39917       [
39918         "Vanuatu",
39919         "vu",
39920         "678"
39921       ],
39922       [
39923         "Vatican City (Città del Vaticano)",
39924         "va",
39925         "39",
39926         1
39927       ],
39928       [
39929         "Venezuela",
39930         "ve",
39931         "58"
39932       ],
39933       [
39934         "Vietnam (Việt Nam)",
39935         "vn",
39936         "84"
39937       ],
39938       [
39939         "Wallis and Futuna (Wallis-et-Futuna)",
39940         "wf",
39941         "681"
39942       ],
39943       [
39944         "Western Sahara (‫الصحراء الغربية‬‎)",
39945         "eh",
39946         "212",
39947         1
39948       ],
39949       [
39950         "Yemen (‫اليمن‬‎)",
39951         "ye",
39952         "967"
39953       ],
39954       [
39955         "Zambia",
39956         "zm",
39957         "260"
39958       ],
39959       [
39960         "Zimbabwe",
39961         "zw",
39962         "263"
39963       ],
39964       [
39965         "Åland Islands",
39966         "ax",
39967         "358",
39968         1
39969       ]
39970   ];
39971   
39972   return d;
39973 }/**
39974 *    This script refer to:
39975 *    Title: International Telephone Input
39976 *    Author: Jack O'Connor
39977 *    Code version:  v12.1.12
39978 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39979 **/
39980
39981 /**
39982  * @class Roo.bootstrap.PhoneInput
39983  * @extends Roo.bootstrap.TriggerField
39984  * An input with International dial-code selection
39985  
39986  * @cfg {String} defaultDialCode default '+852'
39987  * @cfg {Array} preferedCountries default []
39988   
39989  * @constructor
39990  * Create a new PhoneInput.
39991  * @param {Object} config Configuration options
39992  */
39993
39994 Roo.bootstrap.PhoneInput = function(config) {
39995     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39996 };
39997
39998 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39999         
40000         listWidth: undefined,
40001         
40002         selectedClass: 'active',
40003         
40004         invalidClass : "has-warning",
40005         
40006         validClass: 'has-success',
40007         
40008         allowed: '0123456789',
40009         
40010         max_length: 15,
40011         
40012         /**
40013          * @cfg {String} defaultDialCode The default dial code when initializing the input
40014          */
40015         defaultDialCode: '+852',
40016         
40017         /**
40018          * @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
40019          */
40020         preferedCountries: false,
40021         
40022         getAutoCreate : function()
40023         {
40024             var data = Roo.bootstrap.PhoneInputData();
40025             var align = this.labelAlign || this.parentLabelAlign();
40026             var id = Roo.id();
40027             
40028             this.allCountries = [];
40029             this.dialCodeMapping = [];
40030             
40031             for (var i = 0; i < data.length; i++) {
40032               var c = data[i];
40033               this.allCountries[i] = {
40034                 name: c[0],
40035                 iso2: c[1],
40036                 dialCode: c[2],
40037                 priority: c[3] || 0,
40038                 areaCodes: c[4] || null
40039               };
40040               this.dialCodeMapping[c[2]] = {
40041                   name: c[0],
40042                   iso2: c[1],
40043                   priority: c[3] || 0,
40044                   areaCodes: c[4] || null
40045               };
40046             }
40047             
40048             var cfg = {
40049                 cls: 'form-group',
40050                 cn: []
40051             };
40052             
40053             var input =  {
40054                 tag: 'input',
40055                 id : id,
40056                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40057                 maxlength: this.max_length,
40058                 cls : 'form-control tel-input',
40059                 autocomplete: 'new-password'
40060             };
40061             
40062             var hiddenInput = {
40063                 tag: 'input',
40064                 type: 'hidden',
40065                 cls: 'hidden-tel-input'
40066             };
40067             
40068             if (this.name) {
40069                 hiddenInput.name = this.name;
40070             }
40071             
40072             if (this.disabled) {
40073                 input.disabled = true;
40074             }
40075             
40076             var flag_container = {
40077                 tag: 'div',
40078                 cls: 'flag-box',
40079                 cn: [
40080                     {
40081                         tag: 'div',
40082                         cls: 'flag'
40083                     },
40084                     {
40085                         tag: 'div',
40086                         cls: 'caret'
40087                     }
40088                 ]
40089             };
40090             
40091             var box = {
40092                 tag: 'div',
40093                 cls: this.hasFeedback ? 'has-feedback' : '',
40094                 cn: [
40095                     hiddenInput,
40096                     input,
40097                     {
40098                         tag: 'input',
40099                         cls: 'dial-code-holder',
40100                         disabled: true
40101                     }
40102                 ]
40103             };
40104             
40105             var container = {
40106                 cls: 'roo-select2-container input-group',
40107                 cn: [
40108                     flag_container,
40109                     box
40110                 ]
40111             };
40112             
40113             if (this.fieldLabel.length) {
40114                 var indicator = {
40115                     tag: 'i',
40116                     tooltip: 'This field is required'
40117                 };
40118                 
40119                 var label = {
40120                     tag: 'label',
40121                     'for':  id,
40122                     cls: 'control-label',
40123                     cn: []
40124                 };
40125                 
40126                 var label_text = {
40127                     tag: 'span',
40128                     html: this.fieldLabel
40129                 };
40130                 
40131                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40132                 label.cn = [
40133                     indicator,
40134                     label_text
40135                 ];
40136                 
40137                 if(this.indicatorpos == 'right') {
40138                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40139                     label.cn = [
40140                         label_text,
40141                         indicator
40142                     ];
40143                 }
40144                 
40145                 if(align == 'left') {
40146                     container = {
40147                         tag: 'div',
40148                         cn: [
40149                             container
40150                         ]
40151                     };
40152                     
40153                     if(this.labelWidth > 12){
40154                         label.style = "width: " + this.labelWidth + 'px';
40155                     }
40156                     if(this.labelWidth < 13 && this.labelmd == 0){
40157                         this.labelmd = this.labelWidth;
40158                     }
40159                     if(this.labellg > 0){
40160                         label.cls += ' col-lg-' + this.labellg;
40161                         input.cls += ' col-lg-' + (12 - this.labellg);
40162                     }
40163                     if(this.labelmd > 0){
40164                         label.cls += ' col-md-' + this.labelmd;
40165                         container.cls += ' col-md-' + (12 - this.labelmd);
40166                     }
40167                     if(this.labelsm > 0){
40168                         label.cls += ' col-sm-' + this.labelsm;
40169                         container.cls += ' col-sm-' + (12 - this.labelsm);
40170                     }
40171                     if(this.labelxs > 0){
40172                         label.cls += ' col-xs-' + this.labelxs;
40173                         container.cls += ' col-xs-' + (12 - this.labelxs);
40174                     }
40175                 }
40176             }
40177             
40178             cfg.cn = [
40179                 label,
40180                 container
40181             ];
40182             
40183             var settings = this;
40184             
40185             ['xs','sm','md','lg'].map(function(size){
40186                 if (settings[size]) {
40187                     cfg.cls += ' col-' + size + '-' + settings[size];
40188                 }
40189             });
40190             
40191             this.store = new Roo.data.Store({
40192                 proxy : new Roo.data.MemoryProxy({}),
40193                 reader : new Roo.data.JsonReader({
40194                     fields : [
40195                         {
40196                             'name' : 'name',
40197                             'type' : 'string'
40198                         },
40199                         {
40200                             'name' : 'iso2',
40201                             'type' : 'string'
40202                         },
40203                         {
40204                             'name' : 'dialCode',
40205                             'type' : 'string'
40206                         },
40207                         {
40208                             'name' : 'priority',
40209                             'type' : 'string'
40210                         },
40211                         {
40212                             'name' : 'areaCodes',
40213                             'type' : 'string'
40214                         }
40215                     ]
40216                 })
40217             });
40218             
40219             if(!this.preferedCountries) {
40220                 this.preferedCountries = [
40221                     'hk',
40222                     'gb',
40223                     'us'
40224                 ];
40225             }
40226             
40227             var p = this.preferedCountries.reverse();
40228             
40229             if(p) {
40230                 for (var i = 0; i < p.length; i++) {
40231                     for (var j = 0; j < this.allCountries.length; j++) {
40232                         if(this.allCountries[j].iso2 == p[i]) {
40233                             var t = this.allCountries[j];
40234                             this.allCountries.splice(j,1);
40235                             this.allCountries.unshift(t);
40236                         }
40237                     } 
40238                 }
40239             }
40240             
40241             this.store.proxy.data = {
40242                 success: true,
40243                 data: this.allCountries
40244             };
40245             
40246             return cfg;
40247         },
40248         
40249         initEvents : function()
40250         {
40251             this.createList();
40252             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40253             
40254             this.indicator = this.indicatorEl();
40255             this.flag = this.flagEl();
40256             this.dialCodeHolder = this.dialCodeHolderEl();
40257             
40258             this.trigger = this.el.select('div.flag-box',true).first();
40259             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40260             
40261             var _this = this;
40262             
40263             (function(){
40264                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40265                 _this.list.setWidth(lw);
40266             }).defer(100);
40267             
40268             this.list.on('mouseover', this.onViewOver, this);
40269             this.list.on('mousemove', this.onViewMove, this);
40270             this.inputEl().on("keyup", this.onKeyUp, this);
40271             this.inputEl().on("keypress", this.onKeyPress, this);
40272             
40273             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40274
40275             this.view = new Roo.View(this.list, this.tpl, {
40276                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40277             });
40278             
40279             this.view.on('click', this.onViewClick, this);
40280             this.setValue(this.defaultDialCode);
40281         },
40282         
40283         onTriggerClick : function(e)
40284         {
40285             Roo.log('trigger click');
40286             if(this.disabled){
40287                 return;
40288             }
40289             
40290             if(this.isExpanded()){
40291                 this.collapse();
40292                 this.hasFocus = false;
40293             }else {
40294                 this.store.load({});
40295                 this.hasFocus = true;
40296                 this.expand();
40297             }
40298         },
40299         
40300         isExpanded : function()
40301         {
40302             return this.list.isVisible();
40303         },
40304         
40305         collapse : function()
40306         {
40307             if(!this.isExpanded()){
40308                 return;
40309             }
40310             this.list.hide();
40311             Roo.get(document).un('mousedown', this.collapseIf, this);
40312             Roo.get(document).un('mousewheel', this.collapseIf, this);
40313             this.fireEvent('collapse', this);
40314             this.validate();
40315         },
40316         
40317         expand : function()
40318         {
40319             Roo.log('expand');
40320
40321             if(this.isExpanded() || !this.hasFocus){
40322                 return;
40323             }
40324             
40325             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40326             this.list.setWidth(lw);
40327             
40328             this.list.show();
40329             this.restrictHeight();
40330             
40331             Roo.get(document).on('mousedown', this.collapseIf, this);
40332             Roo.get(document).on('mousewheel', this.collapseIf, this);
40333             
40334             this.fireEvent('expand', this);
40335         },
40336         
40337         restrictHeight : function()
40338         {
40339             this.list.alignTo(this.inputEl(), this.listAlign);
40340             this.list.alignTo(this.inputEl(), this.listAlign);
40341         },
40342         
40343         onViewOver : function(e, t)
40344         {
40345             if(this.inKeyMode){
40346                 return;
40347             }
40348             var item = this.view.findItemFromChild(t);
40349             
40350             if(item){
40351                 var index = this.view.indexOf(item);
40352                 this.select(index, false);
40353             }
40354         },
40355
40356         // private
40357         onViewClick : function(view, doFocus, el, e)
40358         {
40359             var index = this.view.getSelectedIndexes()[0];
40360             
40361             var r = this.store.getAt(index);
40362             
40363             if(r){
40364                 this.onSelect(r, index);
40365             }
40366             if(doFocus !== false && !this.blockFocus){
40367                 this.inputEl().focus();
40368             }
40369         },
40370         
40371         onViewMove : function(e, t)
40372         {
40373             this.inKeyMode = false;
40374         },
40375         
40376         select : function(index, scrollIntoView)
40377         {
40378             this.selectedIndex = index;
40379             this.view.select(index);
40380             if(scrollIntoView !== false){
40381                 var el = this.view.getNode(index);
40382                 if(el){
40383                     this.list.scrollChildIntoView(el, false);
40384                 }
40385             }
40386         },
40387         
40388         createList : function()
40389         {
40390             this.list = Roo.get(document.body).createChild({
40391                 tag: 'ul',
40392                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40393                 style: 'display:none'
40394             });
40395             
40396             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40397         },
40398         
40399         collapseIf : function(e)
40400         {
40401             var in_combo  = e.within(this.el);
40402             var in_list =  e.within(this.list);
40403             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40404             
40405             if (in_combo || in_list || is_list) {
40406                 return;
40407             }
40408             this.collapse();
40409         },
40410         
40411         onSelect : function(record, index)
40412         {
40413             if(this.fireEvent('beforeselect', this, record, index) !== false){
40414                 
40415                 this.setFlagClass(record.data.iso2);
40416                 this.setDialCode(record.data.dialCode);
40417                 this.hasFocus = false;
40418                 this.collapse();
40419                 this.fireEvent('select', this, record, index);
40420             }
40421         },
40422         
40423         flagEl : function()
40424         {
40425             var flag = this.el.select('div.flag',true).first();
40426             if(!flag){
40427                 return false;
40428             }
40429             return flag;
40430         },
40431         
40432         dialCodeHolderEl : function()
40433         {
40434             var d = this.el.select('input.dial-code-holder',true).first();
40435             if(!d){
40436                 return false;
40437             }
40438             return d;
40439         },
40440         
40441         setDialCode : function(v)
40442         {
40443             this.dialCodeHolder.dom.value = '+'+v;
40444         },
40445         
40446         setFlagClass : function(n)
40447         {
40448             this.flag.dom.className = 'flag '+n;
40449         },
40450         
40451         getValue : function()
40452         {
40453             var v = this.inputEl().getValue();
40454             if(this.dialCodeHolder) {
40455                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40456             }
40457             return v;
40458         },
40459         
40460         setValue : function(v)
40461         {
40462             var d = this.getDialCode(v);
40463             
40464             //invalid dial code
40465             if(v.length == 0 || !d || d.length == 0) {
40466                 if(this.rendered){
40467                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40468                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40469                 }
40470                 return;
40471             }
40472             
40473             //valid dial code
40474             this.setFlagClass(this.dialCodeMapping[d].iso2);
40475             this.setDialCode(d);
40476             this.inputEl().dom.value = v.replace('+'+d,'');
40477             this.hiddenEl().dom.value = this.getValue();
40478             
40479             this.validate();
40480         },
40481         
40482         getDialCode : function(v)
40483         {
40484             v = v ||  '';
40485             
40486             if (v.length == 0) {
40487                 return this.dialCodeHolder.dom.value;
40488             }
40489             
40490             var dialCode = "";
40491             if (v.charAt(0) != "+") {
40492                 return false;
40493             }
40494             var numericChars = "";
40495             for (var i = 1; i < v.length; i++) {
40496               var c = v.charAt(i);
40497               if (!isNaN(c)) {
40498                 numericChars += c;
40499                 if (this.dialCodeMapping[numericChars]) {
40500                   dialCode = v.substr(1, i);
40501                 }
40502                 if (numericChars.length == 4) {
40503                   break;
40504                 }
40505               }
40506             }
40507             return dialCode;
40508         },
40509         
40510         reset : function()
40511         {
40512             this.setValue(this.defaultDialCode);
40513             this.validate();
40514         },
40515         
40516         hiddenEl : function()
40517         {
40518             return this.el.select('input.hidden-tel-input',true).first();
40519         },
40520         
40521         // after setting val
40522         onKeyUp : function(e){
40523             this.setValue(this.getValue());
40524         },
40525         
40526         onKeyPress : function(e){
40527             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40528                 e.stopEvent();
40529             }
40530         }
40531         
40532 });
40533 /**
40534  * @class Roo.bootstrap.MoneyField
40535  * @extends Roo.bootstrap.ComboBox
40536  * Bootstrap MoneyField class
40537  * 
40538  * @constructor
40539  * Create a new MoneyField.
40540  * @param {Object} config Configuration options
40541  */
40542
40543 Roo.bootstrap.MoneyField = function(config) {
40544     
40545     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40546     
40547 };
40548
40549 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40550     
40551     /**
40552      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40553      */
40554     allowDecimals : true,
40555     /**
40556      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40557      */
40558     decimalSeparator : ".",
40559     /**
40560      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40561      */
40562     decimalPrecision : 0,
40563     /**
40564      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40565      */
40566     allowNegative : true,
40567     /**
40568      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40569      */
40570     allowZero: true,
40571     /**
40572      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40573      */
40574     minValue : Number.NEGATIVE_INFINITY,
40575     /**
40576      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40577      */
40578     maxValue : Number.MAX_VALUE,
40579     /**
40580      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40581      */
40582     minText : "The minimum value for this field is {0}",
40583     /**
40584      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40585      */
40586     maxText : "The maximum value for this field is {0}",
40587     /**
40588      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40589      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40590      */
40591     nanText : "{0} is not a valid number",
40592     /**
40593      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40594      */
40595     castInt : true,
40596     /**
40597      * @cfg {String} defaults currency of the MoneyField
40598      * value should be in lkey
40599      */
40600     defaultCurrency : false,
40601     /**
40602      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40603      */
40604     thousandsDelimiter : false,
40605     /**
40606      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40607      */
40608     max_length: false,
40609     
40610     inputlg : 9,
40611     inputmd : 9,
40612     inputsm : 9,
40613     inputxs : 6,
40614     
40615     store : false,
40616     
40617     getAutoCreate : function()
40618     {
40619         var align = this.labelAlign || this.parentLabelAlign();
40620         
40621         var id = Roo.id();
40622
40623         var cfg = {
40624             cls: 'form-group',
40625             cn: []
40626         };
40627
40628         var input =  {
40629             tag: 'input',
40630             id : id,
40631             cls : 'form-control roo-money-amount-input',
40632             autocomplete: 'new-password'
40633         };
40634         
40635         var hiddenInput = {
40636             tag: 'input',
40637             type: 'hidden',
40638             id: Roo.id(),
40639             cls: 'hidden-number-input'
40640         };
40641         
40642         if(this.max_length) {
40643             input.maxlength = this.max_length; 
40644         }
40645         
40646         if (this.name) {
40647             hiddenInput.name = this.name;
40648         }
40649
40650         if (this.disabled) {
40651             input.disabled = true;
40652         }
40653
40654         var clg = 12 - this.inputlg;
40655         var cmd = 12 - this.inputmd;
40656         var csm = 12 - this.inputsm;
40657         var cxs = 12 - this.inputxs;
40658         
40659         var container = {
40660             tag : 'div',
40661             cls : 'row roo-money-field',
40662             cn : [
40663                 {
40664                     tag : 'div',
40665                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40666                     cn : [
40667                         {
40668                             tag : 'div',
40669                             cls: 'roo-select2-container input-group',
40670                             cn: [
40671                                 {
40672                                     tag : 'input',
40673                                     cls : 'form-control roo-money-currency-input',
40674                                     autocomplete: 'new-password',
40675                                     readOnly : 1,
40676                                     name : this.currencyName
40677                                 },
40678                                 {
40679                                     tag :'span',
40680                                     cls : 'input-group-addon',
40681                                     cn : [
40682                                         {
40683                                             tag: 'span',
40684                                             cls: 'caret'
40685                                         }
40686                                     ]
40687                                 }
40688                             ]
40689                         }
40690                     ]
40691                 },
40692                 {
40693                     tag : 'div',
40694                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40695                     cn : [
40696                         {
40697                             tag: 'div',
40698                             cls: this.hasFeedback ? 'has-feedback' : '',
40699                             cn: [
40700                                 input
40701                             ]
40702                         }
40703                     ]
40704                 }
40705             ]
40706             
40707         };
40708         
40709         if (this.fieldLabel.length) {
40710             var indicator = {
40711                 tag: 'i',
40712                 tooltip: 'This field is required'
40713             };
40714
40715             var label = {
40716                 tag: 'label',
40717                 'for':  id,
40718                 cls: 'control-label',
40719                 cn: []
40720             };
40721
40722             var label_text = {
40723                 tag: 'span',
40724                 html: this.fieldLabel
40725             };
40726
40727             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40728             label.cn = [
40729                 indicator,
40730                 label_text
40731             ];
40732
40733             if(this.indicatorpos == 'right') {
40734                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40735                 label.cn = [
40736                     label_text,
40737                     indicator
40738                 ];
40739             }
40740
40741             if(align == 'left') {
40742                 container = {
40743                     tag: 'div',
40744                     cn: [
40745                         container
40746                     ]
40747                 };
40748
40749                 if(this.labelWidth > 12){
40750                     label.style = "width: " + this.labelWidth + 'px';
40751                 }
40752                 if(this.labelWidth < 13 && this.labelmd == 0){
40753                     this.labelmd = this.labelWidth;
40754                 }
40755                 if(this.labellg > 0){
40756                     label.cls += ' col-lg-' + this.labellg;
40757                     input.cls += ' col-lg-' + (12 - this.labellg);
40758                 }
40759                 if(this.labelmd > 0){
40760                     label.cls += ' col-md-' + this.labelmd;
40761                     container.cls += ' col-md-' + (12 - this.labelmd);
40762                 }
40763                 if(this.labelsm > 0){
40764                     label.cls += ' col-sm-' + this.labelsm;
40765                     container.cls += ' col-sm-' + (12 - this.labelsm);
40766                 }
40767                 if(this.labelxs > 0){
40768                     label.cls += ' col-xs-' + this.labelxs;
40769                     container.cls += ' col-xs-' + (12 - this.labelxs);
40770                 }
40771             }
40772         }
40773
40774         cfg.cn = [
40775             label,
40776             container,
40777             hiddenInput
40778         ];
40779         
40780         var settings = this;
40781
40782         ['xs','sm','md','lg'].map(function(size){
40783             if (settings[size]) {
40784                 cfg.cls += ' col-' + size + '-' + settings[size];
40785             }
40786         });
40787         
40788         return cfg;
40789     },
40790     
40791     initEvents : function()
40792     {
40793         this.indicator = this.indicatorEl();
40794         
40795         this.initCurrencyEvent();
40796         
40797         this.initNumberEvent();
40798     },
40799     
40800     initCurrencyEvent : function()
40801     {
40802         if (!this.store) {
40803             throw "can not find store for combo";
40804         }
40805         
40806         this.store = Roo.factory(this.store, Roo.data);
40807         this.store.parent = this;
40808         
40809         this.createList();
40810         
40811         this.triggerEl = this.el.select('.input-group-addon', true).first();
40812         
40813         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40814         
40815         var _this = this;
40816         
40817         (function(){
40818             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40819             _this.list.setWidth(lw);
40820         }).defer(100);
40821         
40822         this.list.on('mouseover', this.onViewOver, this);
40823         this.list.on('mousemove', this.onViewMove, this);
40824         this.list.on('scroll', this.onViewScroll, this);
40825         
40826         if(!this.tpl){
40827             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40828         }
40829         
40830         this.view = new Roo.View(this.list, this.tpl, {
40831             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40832         });
40833         
40834         this.view.on('click', this.onViewClick, this);
40835         
40836         this.store.on('beforeload', this.onBeforeLoad, this);
40837         this.store.on('load', this.onLoad, this);
40838         this.store.on('loadexception', this.onLoadException, this);
40839         
40840         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40841             "up" : function(e){
40842                 this.inKeyMode = true;
40843                 this.selectPrev();
40844             },
40845
40846             "down" : function(e){
40847                 if(!this.isExpanded()){
40848                     this.onTriggerClick();
40849                 }else{
40850                     this.inKeyMode = true;
40851                     this.selectNext();
40852                 }
40853             },
40854
40855             "enter" : function(e){
40856                 this.collapse();
40857                 
40858                 if(this.fireEvent("specialkey", this, e)){
40859                     this.onViewClick(false);
40860                 }
40861                 
40862                 return true;
40863             },
40864
40865             "esc" : function(e){
40866                 this.collapse();
40867             },
40868
40869             "tab" : function(e){
40870                 this.collapse();
40871                 
40872                 if(this.fireEvent("specialkey", this, e)){
40873                     this.onViewClick(false);
40874                 }
40875                 
40876                 return true;
40877             },
40878
40879             scope : this,
40880
40881             doRelay : function(foo, bar, hname){
40882                 if(hname == 'down' || this.scope.isExpanded()){
40883                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40884                 }
40885                 return true;
40886             },
40887
40888             forceKeyDown: true
40889         });
40890         
40891         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40892         
40893     },
40894     
40895     initNumberEvent : function(e)
40896     {
40897         this.inputEl().on("keydown" , this.fireKey,  this);
40898         this.inputEl().on("focus", this.onFocus,  this);
40899         this.inputEl().on("blur", this.onBlur,  this);
40900         
40901         this.inputEl().relayEvent('keyup', this);
40902         
40903         if(this.indicator){
40904             this.indicator.addClass('invisible');
40905         }
40906  
40907         this.originalValue = this.getValue();
40908         
40909         if(this.validationEvent == 'keyup'){
40910             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40911             this.inputEl().on('keyup', this.filterValidation, this);
40912         }
40913         else if(this.validationEvent !== false){
40914             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40915         }
40916         
40917         if(this.selectOnFocus){
40918             this.on("focus", this.preFocus, this);
40919             
40920         }
40921         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40922             this.inputEl().on("keypress", this.filterKeys, this);
40923         } else {
40924             this.inputEl().relayEvent('keypress', this);
40925         }
40926         
40927         var allowed = "0123456789";
40928         
40929         if(this.allowDecimals){
40930             allowed += this.decimalSeparator;
40931         }
40932         
40933         if(this.allowNegative){
40934             allowed += "-";
40935         }
40936         
40937         if(this.thousandsDelimiter) {
40938             allowed += ",";
40939         }
40940         
40941         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40942         
40943         var keyPress = function(e){
40944             
40945             var k = e.getKey();
40946             
40947             var c = e.getCharCode();
40948             
40949             if(
40950                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40951                     allowed.indexOf(String.fromCharCode(c)) === -1
40952             ){
40953                 e.stopEvent();
40954                 return;
40955             }
40956             
40957             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40958                 return;
40959             }
40960             
40961             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40962                 e.stopEvent();
40963             }
40964         };
40965         
40966         this.inputEl().on("keypress", keyPress, this);
40967         
40968     },
40969     
40970     onTriggerClick : function(e)
40971     {   
40972         if(this.disabled){
40973             return;
40974         }
40975         
40976         this.page = 0;
40977         this.loadNext = false;
40978         
40979         if(this.isExpanded()){
40980             this.collapse();
40981             return;
40982         }
40983         
40984         this.hasFocus = true;
40985         
40986         if(this.triggerAction == 'all') {
40987             this.doQuery(this.allQuery, true);
40988             return;
40989         }
40990         
40991         this.doQuery(this.getRawValue());
40992     },
40993     
40994     getCurrency : function()
40995     {   
40996         var v = this.currencyEl().getValue();
40997         
40998         return v;
40999     },
41000     
41001     restrictHeight : function()
41002     {
41003         this.list.alignTo(this.currencyEl(), this.listAlign);
41004         this.list.alignTo(this.currencyEl(), this.listAlign);
41005     },
41006     
41007     onViewClick : function(view, doFocus, el, e)
41008     {
41009         var index = this.view.getSelectedIndexes()[0];
41010         
41011         var r = this.store.getAt(index);
41012         
41013         if(r){
41014             this.onSelect(r, index);
41015         }
41016     },
41017     
41018     onSelect : function(record, index){
41019         
41020         if(this.fireEvent('beforeselect', this, record, index) !== false){
41021         
41022             this.setFromCurrencyData(index > -1 ? record.data : false);
41023             
41024             this.collapse();
41025             
41026             this.fireEvent('select', this, record, index);
41027         }
41028     },
41029     
41030     setFromCurrencyData : function(o)
41031     {
41032         var currency = '';
41033         
41034         this.lastCurrency = o;
41035         
41036         if (this.currencyField) {
41037             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41038         } else {
41039             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41040         }
41041         
41042         this.lastSelectionText = currency;
41043         
41044         //setting default currency
41045         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41046             this.setCurrency(this.defaultCurrency);
41047             return;
41048         }
41049         
41050         this.setCurrency(currency);
41051     },
41052     
41053     setFromData : function(o)
41054     {
41055         var c = {};
41056         
41057         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41058         
41059         this.setFromCurrencyData(c);
41060         
41061         var value = '';
41062         
41063         if (this.name) {
41064             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41065         } else {
41066             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41067         }
41068         
41069         this.setValue(value);
41070         
41071     },
41072     
41073     setCurrency : function(v)
41074     {   
41075         this.currencyValue = v;
41076         
41077         if(this.rendered){
41078             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41079             this.validate();
41080         }
41081     },
41082     
41083     setValue : function(v)
41084     {
41085         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41086         
41087         this.value = v;
41088         
41089         if(this.rendered){
41090             
41091             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41092             
41093             this.inputEl().dom.value = (v == '') ? '' :
41094                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41095             
41096             if(!this.allowZero && v === '0') {
41097                 this.hiddenEl().dom.value = '';
41098                 this.inputEl().dom.value = '';
41099             }
41100             
41101             this.validate();
41102         }
41103     },
41104     
41105     getRawValue : function()
41106     {
41107         var v = this.inputEl().getValue();
41108         
41109         return v;
41110     },
41111     
41112     getValue : function()
41113     {
41114         return this.fixPrecision(this.parseValue(this.getRawValue()));
41115     },
41116     
41117     parseValue : function(value)
41118     {
41119         if(this.thousandsDelimiter) {
41120             value += "";
41121             r = new RegExp(",", "g");
41122             value = value.replace(r, "");
41123         }
41124         
41125         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41126         return isNaN(value) ? '' : value;
41127         
41128     },
41129     
41130     fixPrecision : function(value)
41131     {
41132         if(this.thousandsDelimiter) {
41133             value += "";
41134             r = new RegExp(",", "g");
41135             value = value.replace(r, "");
41136         }
41137         
41138         var nan = isNaN(value);
41139         
41140         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41141             return nan ? '' : value;
41142         }
41143         return parseFloat(value).toFixed(this.decimalPrecision);
41144     },
41145     
41146     decimalPrecisionFcn : function(v)
41147     {
41148         return Math.floor(v);
41149     },
41150     
41151     validateValue : function(value)
41152     {
41153         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41154             return false;
41155         }
41156         
41157         var num = this.parseValue(value);
41158         
41159         if(isNaN(num)){
41160             this.markInvalid(String.format(this.nanText, value));
41161             return false;
41162         }
41163         
41164         if(num < this.minValue){
41165             this.markInvalid(String.format(this.minText, this.minValue));
41166             return false;
41167         }
41168         
41169         if(num > this.maxValue){
41170             this.markInvalid(String.format(this.maxText, this.maxValue));
41171             return false;
41172         }
41173         
41174         return true;
41175     },
41176     
41177     validate : function()
41178     {
41179         if(this.disabled || this.allowBlank){
41180             this.markValid();
41181             return true;
41182         }
41183         
41184         var currency = this.getCurrency();
41185         
41186         if(this.validateValue(this.getRawValue()) && currency.length){
41187             this.markValid();
41188             return true;
41189         }
41190         
41191         this.markInvalid();
41192         return false;
41193     },
41194     
41195     getName: function()
41196     {
41197         return this.name;
41198     },
41199     
41200     beforeBlur : function()
41201     {
41202         if(!this.castInt){
41203             return;
41204         }
41205         
41206         var v = this.parseValue(this.getRawValue());
41207         
41208         if(v || v == 0){
41209             this.setValue(v);
41210         }
41211     },
41212     
41213     onBlur : function()
41214     {
41215         this.beforeBlur();
41216         
41217         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41218             //this.el.removeClass(this.focusClass);
41219         }
41220         
41221         this.hasFocus = false;
41222         
41223         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41224             this.validate();
41225         }
41226         
41227         var v = this.getValue();
41228         
41229         if(String(v) !== String(this.startValue)){
41230             this.fireEvent('change', this, v, this.startValue);
41231         }
41232         
41233         this.fireEvent("blur", this);
41234     },
41235     
41236     inputEl : function()
41237     {
41238         return this.el.select('.roo-money-amount-input', true).first();
41239     },
41240     
41241     currencyEl : function()
41242     {
41243         return this.el.select('.roo-money-currency-input', true).first();
41244     },
41245     
41246     hiddenEl : function()
41247     {
41248         return this.el.select('input.hidden-number-input',true).first();
41249     }
41250     
41251 });