Roo/bootstrap/Navbar.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fs
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     fa: '',
653     badge: '',
654     theme: 'default',
655     inverse: false,
656     
657     toggle: false,
658     ontext: 'ON',
659     offtext: 'OFF',
660     defaulton: true,
661     preventDefault: true,
662     removeClass: false,
663     name: false,
664     target: false,
665      
666     pressed : null,
667      
668     
669     getAutoCreate : function(){
670         
671         var cfg = {
672             tag : 'button',
673             cls : 'roo-button',
674             html: ''
675         };
676         
677         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
679             this.tag = 'button';
680         } else {
681             cfg.tag = this.tag;
682         }
683         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684         
685         if (this.toggle == true) {
686             cfg={
687                 tag: 'div',
688                 cls: 'slider-frame roo-button',
689                 cn: [
690                     {
691                         tag: 'span',
692                         'data-on-text':'ON',
693                         'data-off-text':'OFF',
694                         cls: 'slider-button',
695                         html: this.offtext
696                     }
697                 ]
698             };
699             
700             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701                 cfg.cls += ' '+this.weight;
702             }
703             
704             return cfg;
705         }
706         
707         if (this.isClose) {
708             cfg.cls += ' close';
709             
710             cfg["aria-hidden"] = true;
711             
712             cfg.html = "&times;";
713             
714             return cfg;
715         }
716         
717          
718         if (this.theme==='default') {
719             cfg.cls = 'btn roo-button';
720             
721             //if (this.parentType != 'Navbar') {
722             this.weight = this.weight.length ?  this.weight : 'default';
723             //}
724             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725                 
726                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728                 cfg.cls += ' btn-' + outline + weight;
729                 if (this.weight == 'default') {
730                     // BC
731                     cfg.cls += ' btn-' + this.weight;
732                 }
733             }
734         } else if (this.theme==='glow') {
735             
736             cfg.tag = 'a';
737             cfg.cls = 'btn-glow roo-button';
738             
739             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 cfg.cls += ' ' + this.weight;
742             }
743         }
744    
745         
746         if (this.inverse) {
747             this.cls += ' inverse';
748         }
749         
750         
751         if (this.active || this.pressed === true) {
752             cfg.cls += ' active';
753         }
754         
755         if (this.disabled) {
756             cfg.disabled = 'disabled';
757         }
758         
759         if (this.items) {
760             Roo.log('changing to ul' );
761             cfg.tag = 'ul';
762             this.glyphicon = 'caret';
763             if (Roo.bootstrap.version == 4) {
764                 this.fa = 'caret-down';
765             }
766             
767         }
768         
769         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
770          
771         //gsRoo.log(this.parentType);
772         if (this.parentType === 'Navbar' && !this.parent().bar) {
773             Roo.log('changing to li?');
774             
775             cfg.tag = 'li';
776             
777             cfg.cls = '';
778             cfg.cn =  [{
779                 tag : 'a',
780                 cls : 'roo-button',
781                 html : this.html,
782                 href : this.href || '#'
783             }];
784             if (this.menu) {
785                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
786                 cfg.cls += ' dropdown';
787             }   
788             
789             delete cfg.html;
790             
791         }
792         
793        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
794         
795         if (this.glyphicon) {
796             cfg.html = ' ' + cfg.html;
797             
798             cfg.cn = [
799                 {
800                     tag: 'span',
801                     cls: 'glyphicon glyphicon-' + this.glyphicon
802                 }
803             ];
804         }
805         if (this.fa) {
806             cfg.html = ' ' + cfg.html;
807             
808             cfg.cn = [
809                 {
810                     tag: 'i',
811                     cls: 'fa fas fa-' + this.fa
812                 }
813             ];
814         }
815         
816         if (this.badge) {
817             cfg.html += ' ';
818             
819             cfg.tag = 'a';
820             
821 //            cfg.cls='btn roo-button';
822             
823             cfg.href=this.href;
824             
825             var value = cfg.html;
826             
827             if(this.glyphicon){
828                 value = {
829                     tag: 'span',
830                     cls: 'glyphicon glyphicon-' + this.glyphicon,
831                     html: this.html
832                 };
833             }
834             if(this.fa){
835                 value = {
836                     tag: 'i',
837                     cls: 'fa fas fa-' + this.fa,
838                     html: this.html
839                 };
840             }
841             
842             var bw = this.badge_weight.length ? this.badge_weight :
843                 (this.weight.length ? this.weight : 'secondary');
844             bw = bw == 'default' ? 'secondary' : bw;
845             
846             cfg.cn = [
847                 value,
848                 {
849                     tag: 'span',
850                     cls: 'badge badge-' + bw,
851                     html: this.badge
852                 }
853             ];
854             
855             cfg.html='';
856         }
857         
858         if (this.menu) {
859             cfg.cls += ' dropdown';
860             cfg.html = typeof(cfg.html) != 'undefined' ?
861                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
862         }
863         
864         if (cfg.tag !== 'a' && this.href !== '') {
865             throw "Tag must be a to set href.";
866         } else if (this.href.length > 0) {
867             cfg.href = this.href;
868         }
869         
870         if(this.removeClass){
871             cfg.cls = '';
872         }
873         
874         if(this.target){
875             cfg.target = this.target;
876         }
877         
878         return cfg;
879     },
880     initEvents: function() {
881        // Roo.log('init events?');
882 //        Roo.log(this.el.dom);
883         // add the menu...
884         
885         if (typeof (this.menu) != 'undefined') {
886             this.menu.parentType = this.xtype;
887             this.menu.triggerEl = this.el;
888             this.addxtype(Roo.apply({}, this.menu));
889         }
890
891
892        if (this.el.hasClass('roo-button')) {
893             this.el.on('click', this.onClick, this);
894        } else {
895             this.el.select('.roo-button').on('click', this.onClick, this);
896        }
897        
898        if(this.removeClass){
899            this.el.on('click', this.onClick, this);
900        }
901        
902        this.el.enableDisplayMode();
903         
904     },
905     onClick : function(e)
906     {
907         if (this.disabled) {
908             return;
909         }
910         
911         Roo.log('button on click ');
912         if(this.preventDefault){
913             e.preventDefault();
914         }
915         
916         if (this.pressed === true || this.pressed === false) {
917             this.toggleActive(e);
918         }
919         
920         
921         this.fireEvent('click', this, e);
922     },
923     
924     /**
925      * Enables this button
926      */
927     enable : function()
928     {
929         this.disabled = false;
930         this.el.removeClass('disabled');
931     },
932     
933     /**
934      * Disable this button
935      */
936     disable : function()
937     {
938         this.disabled = true;
939         this.el.addClass('disabled');
940     },
941      /**
942      * sets the active state on/off, 
943      * @param {Boolean} state (optional) Force a particular state
944      */
945     setActive : function(v) {
946         
947         this.el[v ? 'addClass' : 'removeClass']('active');
948         this.pressed = v;
949     },
950      /**
951      * toggles the current active state 
952      */
953     toggleActive : function(e)
954     {
955         this.setActive(!this.pressed);
956         this.fireEvent('toggle', this, e, !this.pressed);
957     },
958      /**
959      * get the current active state
960      * @return {boolean} true if it's active
961      */
962     isActive : function()
963     {
964         return this.el.hasClass('active');
965     },
966     /**
967      * set the text of the first selected button
968      */
969     setText : function(str)
970     {
971         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
972     },
973     /**
974      * get the text of the first selected button
975      */
976     getText : function()
977     {
978         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
979     },
980     
981     setWeight : function(str)
982     {
983         this.el.removeClass(this.weightClass);
984         this.weight = str;
985         var outline = this.outline ? 'outline-' : '';
986         if (str == 'default') {
987             this.el.addClass('btn-default btn-outline-secondary');        
988             return;
989         }
990         this.el.addClass('btn-' + outline + str);        
991     }
992     
993     
994 });
995
996  /*
997  * - LGPL
998  *
999  * column
1000  * 
1001  */
1002
1003 /**
1004  * @class Roo.bootstrap.Column
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Column class
1007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1015  *
1016  * 
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019  * @cfg {String} fa (ban|check|...) font awesome icon
1020  * @cfg {Number} fasize (1|2|....) font awsome size
1021
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023
1024  * @cfg {String} html content of column.
1025  * 
1026  * @constructor
1027  * Create a new Column
1028  * @param {Object} config The config object
1029  */
1030
1031 Roo.bootstrap.Column = function(config){
1032     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1033 };
1034
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1036     
1037     xs: false,
1038     sm: false,
1039     md: false,
1040     lg: false,
1041     xsoff: false,
1042     smoff: false,
1043     mdoff: false,
1044     lgoff: false,
1045     html: '',
1046     offset: 0,
1047     alert: false,
1048     fa: false,
1049     icon : false,
1050     hidden : false,
1051     fasize : 1,
1052     
1053     getAutoCreate : function(){
1054         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1055         
1056         cfg = {
1057             tag: 'div',
1058             cls: 'column'
1059         };
1060         
1061         var settings=this;
1062         ['xs','sm','md','lg'].map(function(size){
1063             //Roo.log( size + ':' + settings[size]);
1064             
1065             if (settings[size+'off'] !== false) {
1066                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1067             }
1068             
1069             if (settings[size] === false) {
1070                 return;
1071             }
1072             
1073             if (!settings[size]) { // 0 = hidden
1074                 cfg.cls += ' hidden-' + size;
1075                 return;
1076             }
1077             cfg.cls += ' col-' + size + '-' + settings[size];
1078             
1079         });
1080         
1081         if (this.hidden) {
1082             cfg.cls += ' hidden';
1083         }
1084         
1085         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086             cfg.cls +=' alert alert-' + this.alert;
1087         }
1088         
1089         
1090         if (this.html.length) {
1091             cfg.html = this.html;
1092         }
1093         if (this.fa) {
1094             var fasize = '';
1095             if (this.fasize > 1) {
1096                 fasize = ' fa-' + this.fasize + 'x';
1097             }
1098             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1099             
1100             
1101         }
1102         if (this.icon) {
1103             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1104         }
1105         
1106         return cfg;
1107     }
1108    
1109 });
1110
1111  
1112
1113  /*
1114  * - LGPL
1115  *
1116  * page container.
1117  * 
1118  */
1119
1120
1121 /**
1122  * @class Roo.bootstrap.Container
1123  * @extends Roo.bootstrap.Component
1124  * Bootstrap Container class
1125  * @cfg {Boolean} jumbotron is it a jumbotron element
1126  * @cfg {String} html content of element
1127  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1129  * @cfg {String} header content of header (for panel)
1130  * @cfg {String} footer content of footer (for panel)
1131  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132  * @cfg {String} tag (header|aside|section) type of HTML tag.
1133  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134  * @cfg {String} fa font awesome icon
1135  * @cfg {String} icon (info-sign|check|...) glyphicon name
1136  * @cfg {Boolean} hidden (true|false) hide the element
1137  * @cfg {Boolean} expandable (true|false) default false
1138  * @cfg {Boolean} expanded (true|false) default true
1139  * @cfg {String} rheader contet on the right of header
1140  * @cfg {Boolean} clickable (true|false) default false
1141
1142  *     
1143  * @constructor
1144  * Create a new Container
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Container = function(config){
1149     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // raw events
1153          /**
1154          * @event expand
1155          * After the panel has been expand
1156          * 
1157          * @param {Roo.bootstrap.Container} this
1158          */
1159         "expand" : true,
1160         /**
1161          * @event collapse
1162          * After the panel has been collapsed
1163          * 
1164          * @param {Roo.bootstrap.Container} this
1165          */
1166         "collapse" : true,
1167         /**
1168          * @event click
1169          * When a element is chick
1170          * @param {Roo.bootstrap.Container} this
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1178     
1179     jumbotron : false,
1180     well: '',
1181     panel : '',
1182     header: '',
1183     footer : '',
1184     sticky: '',
1185     tag : false,
1186     alert : false,
1187     fa: false,
1188     icon : false,
1189     expandable : false,
1190     rheader : '',
1191     expanded : true,
1192     clickable: false,
1193   
1194      
1195     getChildContainer : function() {
1196         
1197         if(!this.el){
1198             return false;
1199         }
1200         
1201         if (this.panel.length) {
1202             return this.el.select('.panel-body',true).first();
1203         }
1204         
1205         return this.el;
1206     },
1207     
1208     
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag : this.tag || 'div',
1213             html : '',
1214             cls : ''
1215         };
1216         if (this.jumbotron) {
1217             cfg.cls = 'jumbotron';
1218         }
1219         
1220         
1221         
1222         // - this is applied by the parent..
1223         //if (this.cls) {
1224         //    cfg.cls = this.cls + '';
1225         //}
1226         
1227         if (this.sticky.length) {
1228             
1229             var bd = Roo.get(document.body);
1230             if (!bd.hasClass('bootstrap-sticky')) {
1231                 bd.addClass('bootstrap-sticky');
1232                 Roo.select('html',true).setStyle('height', '100%');
1233             }
1234              
1235             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1236         }
1237         
1238         
1239         if (this.well.length) {
1240             switch (this.well) {
1241                 case 'lg':
1242                 case 'sm':
1243                     cfg.cls +=' well well-' +this.well;
1244                     break;
1245                 default:
1246                     cfg.cls +=' well';
1247                     break;
1248             }
1249         }
1250         
1251         if (this.hidden) {
1252             cfg.cls += ' hidden';
1253         }
1254         
1255         
1256         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257             cfg.cls +=' alert alert-' + this.alert;
1258         }
1259         
1260         var body = cfg;
1261         
1262         if (this.panel.length) {
1263             cfg.cls += ' panel panel-' + this.panel;
1264             cfg.cn = [];
1265             if (this.header.length) {
1266                 
1267                 var h = [];
1268                 
1269                 if(this.expandable){
1270                     
1271                     cfg.cls = cfg.cls + ' expandable';
1272                     
1273                     h.push({
1274                         tag: 'i',
1275                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1276                     });
1277                     
1278                 }
1279                 
1280                 h.push(
1281                     {
1282                         tag: 'span',
1283                         cls : 'panel-title',
1284                         html : (this.expandable ? '&nbsp;' : '') + this.header
1285                     },
1286                     {
1287                         tag: 'span',
1288                         cls: 'panel-header-right',
1289                         html: this.rheader
1290                     }
1291                 );
1292                 
1293                 cfg.cn.push({
1294                     cls : 'panel-heading',
1295                     style : this.expandable ? 'cursor: pointer' : '',
1296                     cn : h
1297                 });
1298                 
1299             }
1300             
1301             body = false;
1302             cfg.cn.push({
1303                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1304                 html : this.html
1305             });
1306             
1307             
1308             if (this.footer.length) {
1309                 cfg.cn.push({
1310                     cls : 'panel-footer',
1311                     html : this.footer
1312                     
1313                 });
1314             }
1315             
1316         }
1317         
1318         if (body) {
1319             body.html = this.html || cfg.html;
1320             // prefix with the icons..
1321             if (this.fa) {
1322                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1323             }
1324             if (this.icon) {
1325                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1326             }
1327             
1328             
1329         }
1330         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331             cfg.cls =  'container';
1332         }
1333         
1334         return cfg;
1335     },
1336     
1337     initEvents: function() 
1338     {
1339         if(this.expandable){
1340             var headerEl = this.headerEl();
1341         
1342             if(headerEl){
1343                 headerEl.on('click', this.onToggleClick, this);
1344             }
1345         }
1346         
1347         if(this.clickable){
1348             this.el.on('click', this.onClick, this);
1349         }
1350         
1351     },
1352     
1353     onToggleClick : function()
1354     {
1355         var headerEl = this.headerEl();
1356         
1357         if(!headerEl){
1358             return;
1359         }
1360         
1361         if(this.expanded){
1362             this.collapse();
1363             return;
1364         }
1365         
1366         this.expand();
1367     },
1368     
1369     expand : function()
1370     {
1371         if(this.fireEvent('expand', this)) {
1372             
1373             this.expanded = true;
1374             
1375             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1376             
1377             this.el.select('.panel-body',true).first().removeClass('hide');
1378             
1379             var toggleEl = this.toggleEl();
1380
1381             if(!toggleEl){
1382                 return;
1383             }
1384
1385             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1386         }
1387         
1388     },
1389     
1390     collapse : function()
1391     {
1392         if(this.fireEvent('collapse', this)) {
1393             
1394             this.expanded = false;
1395             
1396             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397             this.el.select('.panel-body',true).first().addClass('hide');
1398         
1399             var toggleEl = this.toggleEl();
1400
1401             if(!toggleEl){
1402                 return;
1403             }
1404
1405             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1406         }
1407     },
1408     
1409     toggleEl : function()
1410     {
1411         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1412             return;
1413         }
1414         
1415         return this.el.select('.panel-heading .fa',true).first();
1416     },
1417     
1418     headerEl : function()
1419     {
1420         if(!this.el || !this.panel.length || !this.header.length){
1421             return;
1422         }
1423         
1424         return this.el.select('.panel-heading',true).first()
1425     },
1426     
1427     bodyEl : function()
1428     {
1429         if(!this.el || !this.panel.length){
1430             return;
1431         }
1432         
1433         return this.el.select('.panel-body',true).first()
1434     },
1435     
1436     titleEl : function()
1437     {
1438         if(!this.el || !this.panel.length || !this.header.length){
1439             return;
1440         }
1441         
1442         return this.el.select('.panel-title',true).first();
1443     },
1444     
1445     setTitle : function(v)
1446     {
1447         var titleEl = this.titleEl();
1448         
1449         if(!titleEl){
1450             return;
1451         }
1452         
1453         titleEl.dom.innerHTML = v;
1454     },
1455     
1456     getTitle : function()
1457     {
1458         
1459         var titleEl = this.titleEl();
1460         
1461         if(!titleEl){
1462             return '';
1463         }
1464         
1465         return titleEl.dom.innerHTML;
1466     },
1467     
1468     setRightTitle : function(v)
1469     {
1470         var t = this.el.select('.panel-header-right',true).first();
1471         
1472         if(!t){
1473             return;
1474         }
1475         
1476         t.dom.innerHTML = v;
1477     },
1478     
1479     onClick : function(e)
1480     {
1481         e.preventDefault();
1482         
1483         this.fireEvent('click', this, e);
1484     }
1485 });
1486
1487  /*
1488  * - LGPL
1489  *
1490  * image
1491  * 
1492  */
1493
1494
1495 /**
1496  * @class Roo.bootstrap.Img
1497  * @extends Roo.bootstrap.Component
1498  * Bootstrap Img class
1499  * @cfg {Boolean} imgResponsive false | true
1500  * @cfg {String} border rounded | circle | thumbnail
1501  * @cfg {String} src image source
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505  * @cfg {String} xsUrl xs image source
1506  * @cfg {String} smUrl sm image source
1507  * @cfg {String} mdUrl md image source
1508  * @cfg {String} lgUrl lg image source
1509  * 
1510  * @constructor
1511  * Create a new Input
1512  * @param {Object} config The config object
1513  */
1514
1515 Roo.bootstrap.Img = function(config){
1516     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1517     
1518     this.addEvents({
1519         // img events
1520         /**
1521          * @event click
1522          * The img click event for the img.
1523          * @param {Roo.EventObject} e
1524          */
1525         "click" : true
1526     });
1527 };
1528
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1530     
1531     imgResponsive: true,
1532     border: '',
1533     src: 'about:blank',
1534     href: false,
1535     target: false,
1536     xsUrl: '',
1537     smUrl: '',
1538     mdUrl: '',
1539     lgUrl: '',
1540
1541     getAutoCreate : function()
1542     {   
1543         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544             return this.createSingleImg();
1545         }
1546         
1547         var cfg = {
1548             tag: 'div',
1549             cls: 'roo-image-responsive-group',
1550             cn: []
1551         };
1552         var _this = this;
1553         
1554         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1555             
1556             if(!_this[size + 'Url']){
1557                 return;
1558             }
1559             
1560             var img = {
1561                 tag: 'img',
1562                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563                 html: _this.html || cfg.html,
1564                 src: _this[size + 'Url']
1565             };
1566             
1567             img.cls += ' roo-image-responsive-' + size;
1568             
1569             var s = ['xs', 'sm', 'md', 'lg'];
1570             
1571             s.splice(s.indexOf(size), 1);
1572             
1573             Roo.each(s, function(ss){
1574                 img.cls += ' hidden-' + ss;
1575             });
1576             
1577             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578                 cfg.cls += ' img-' + _this.border;
1579             }
1580             
1581             if(_this.alt){
1582                 cfg.alt = _this.alt;
1583             }
1584             
1585             if(_this.href){
1586                 var a = {
1587                     tag: 'a',
1588                     href: _this.href,
1589                     cn: [
1590                         img
1591                     ]
1592                 };
1593
1594                 if(this.target){
1595                     a.target = _this.target;
1596                 }
1597             }
1598             
1599             cfg.cn.push((_this.href) ? a : img);
1600             
1601         });
1602         
1603         return cfg;
1604     },
1605     
1606     createSingleImg : function()
1607     {
1608         var cfg = {
1609             tag: 'img',
1610             cls: (this.imgResponsive) ? 'img-responsive' : '',
1611             html : null,
1612             src : 'about:blank'  // just incase src get's set to undefined?!?
1613         };
1614         
1615         cfg.html = this.html || cfg.html;
1616         
1617         cfg.src = this.src || cfg.src;
1618         
1619         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620             cfg.cls += ' img-' + this.border;
1621         }
1622         
1623         if(this.alt){
1624             cfg.alt = this.alt;
1625         }
1626         
1627         if(this.href){
1628             var a = {
1629                 tag: 'a',
1630                 href: this.href,
1631                 cn: [
1632                     cfg
1633                 ]
1634             };
1635             
1636             if(this.target){
1637                 a.target = this.target;
1638             }
1639             
1640         }
1641         
1642         return (this.href) ? a : cfg;
1643     },
1644     
1645     initEvents: function() 
1646     {
1647         if(!this.href){
1648             this.el.on('click', this.onClick, this);
1649         }
1650         
1651     },
1652     
1653     onClick : function(e)
1654     {
1655         Roo.log('img onclick');
1656         this.fireEvent('click', this, e);
1657     },
1658     /**
1659      * Sets the url of the image - used to update it
1660      * @param {String} url the url of the image
1661      */
1662     
1663     setSrc : function(url)
1664     {
1665         this.src =  url;
1666         
1667         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668             this.el.dom.src =  url;
1669             return;
1670         }
1671         
1672         this.el.select('img', true).first().dom.src =  url;
1673     }
1674     
1675     
1676    
1677 });
1678
1679  /*
1680  * - LGPL
1681  *
1682  * image
1683  * 
1684  */
1685
1686
1687 /**
1688  * @class Roo.bootstrap.Link
1689  * @extends Roo.bootstrap.Component
1690  * Bootstrap Link Class
1691  * @cfg {String} alt image alternative text
1692  * @cfg {String} href a tag href
1693  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694  * @cfg {String} html the content of the link.
1695  * @cfg {String} anchor name for the anchor link
1696  * @cfg {String} fa - favicon
1697
1698  * @cfg {Boolean} preventDefault (true | false) default false
1699
1700  * 
1701  * @constructor
1702  * Create a new Input
1703  * @param {Object} config The config object
1704  */
1705
1706 Roo.bootstrap.Link = function(config){
1707     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1708     
1709     this.addEvents({
1710         // img events
1711         /**
1712          * @event click
1713          * The img click event for the img.
1714          * @param {Roo.EventObject} e
1715          */
1716         "click" : true
1717     });
1718 };
1719
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1721     
1722     href: false,
1723     target: false,
1724     preventDefault: false,
1725     anchor : false,
1726     alt : false,
1727     fa: false,
1728
1729
1730     getAutoCreate : function()
1731     {
1732         var html = this.html || '';
1733         
1734         if (this.fa !== false) {
1735             html = '<i class="fa fa-' + this.fa + '"></i>';
1736         }
1737         var cfg = {
1738             tag: 'a'
1739         };
1740         // anchor's do not require html/href...
1741         if (this.anchor === false) {
1742             cfg.html = html;
1743             cfg.href = this.href || '#';
1744         } else {
1745             cfg.name = this.anchor;
1746             if (this.html !== false || this.fa !== false) {
1747                 cfg.html = html;
1748             }
1749             if (this.href !== false) {
1750                 cfg.href = this.href;
1751             }
1752         }
1753         
1754         if(this.alt !== false){
1755             cfg.alt = this.alt;
1756         }
1757         
1758         
1759         if(this.target !== false) {
1760             cfg.target = this.target;
1761         }
1762         
1763         return cfg;
1764     },
1765     
1766     initEvents: function() {
1767         
1768         if(!this.href || this.preventDefault){
1769             this.el.on('click', this.onClick, this);
1770         }
1771     },
1772     
1773     onClick : function(e)
1774     {
1775         if(this.preventDefault){
1776             e.preventDefault();
1777         }
1778         //Roo.log('img onclick');
1779         this.fireEvent('click', this, e);
1780     }
1781    
1782 });
1783
1784  /*
1785  * - LGPL
1786  *
1787  * header
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Header
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Header class
1795  * @cfg {String} html content of header
1796  * @cfg {Number} level (1|2|3|4|5|6) default 1
1797  * 
1798  * @constructor
1799  * Create a new Header
1800  * @param {Object} config The config object
1801  */
1802
1803
1804 Roo.bootstrap.Header  = function(config){
1805     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1806 };
1807
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1809     
1810     //href : false,
1811     html : false,
1812     level : 1,
1813     
1814     
1815     
1816     getAutoCreate : function(){
1817         
1818         
1819         
1820         var cfg = {
1821             tag: 'h' + (1 *this.level),
1822             html: this.html || ''
1823         } ;
1824         
1825         return cfg;
1826     }
1827    
1828 });
1829
1830  
1831
1832  /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842  
1843 /**
1844  * @class Roo.bootstrap.MenuMgr
1845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1846  * @singleton
1847  */
1848 Roo.bootstrap.MenuMgr = function(){
1849    var menus, active, groups = {}, attached = false, lastShow = new Date();
1850
1851    // private - called when first menu is created
1852    function init(){
1853        menus = {};
1854        active = new Roo.util.MixedCollection();
1855        Roo.get(document).addKeyListener(27, function(){
1856            if(active.length > 0){
1857                hideAll();
1858            }
1859        });
1860    }
1861
1862    // private
1863    function hideAll(){
1864        if(active && active.length > 0){
1865            var c = active.clone();
1866            c.each(function(m){
1867                m.hide();
1868            });
1869        }
1870    }
1871
1872    // private
1873    function onHide(m){
1874        active.remove(m);
1875        if(active.length < 1){
1876            Roo.get(document).un("mouseup", onMouseDown);
1877             
1878            attached = false;
1879        }
1880    }
1881
1882    // private
1883    function onShow(m){
1884        var last = active.last();
1885        lastShow = new Date();
1886        active.add(m);
1887        if(!attached){
1888           Roo.get(document).on("mouseup", onMouseDown);
1889            
1890            attached = true;
1891        }
1892        if(m.parentMenu){
1893           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894           m.parentMenu.activeChild = m;
1895        }else if(last && last.isVisible()){
1896           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1897        }
1898    }
1899
1900    // private
1901    function onBeforeHide(m){
1902        if(m.activeChild){
1903            m.activeChild.hide();
1904        }
1905        if(m.autoHideTimer){
1906            clearTimeout(m.autoHideTimer);
1907            delete m.autoHideTimer;
1908        }
1909    }
1910
1911    // private
1912    function onBeforeShow(m){
1913        var pm = m.parentMenu;
1914        if(!pm && !m.allowOtherMenus){
1915            hideAll();
1916        }else if(pm && pm.activeChild && active != m){
1917            pm.activeChild.hide();
1918        }
1919    }
1920
1921    // private this should really trigger on mouseup..
1922    function onMouseDown(e){
1923         Roo.log("on Mouse Up");
1924         
1925         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926             Roo.log("MenuManager hideAll");
1927             hideAll();
1928             e.stopEvent();
1929         }
1930         
1931         
1932    }
1933
1934    // private
1935    function onBeforeCheck(mi, state){
1936        if(state){
1937            var g = groups[mi.group];
1938            for(var i = 0, l = g.length; i < l; i++){
1939                if(g[i] != mi){
1940                    g[i].setChecked(false);
1941                }
1942            }
1943        }
1944    }
1945
1946    return {
1947
1948        /**
1949         * Hides all menus that are currently visible
1950         */
1951        hideAll : function(){
1952             hideAll();  
1953        },
1954
1955        // private
1956        register : function(menu){
1957            if(!menus){
1958                init();
1959            }
1960            menus[menu.id] = menu;
1961            menu.on("beforehide", onBeforeHide);
1962            menu.on("hide", onHide);
1963            menu.on("beforeshow", onBeforeShow);
1964            menu.on("show", onShow);
1965            var g = menu.group;
1966            if(g && menu.events["checkchange"]){
1967                if(!groups[g]){
1968                    groups[g] = [];
1969                }
1970                groups[g].push(menu);
1971                menu.on("checkchange", onCheck);
1972            }
1973        },
1974
1975         /**
1976          * Returns a {@link Roo.menu.Menu} object
1977          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978          * be used to generate and return a new Menu instance.
1979          */
1980        get : function(menu){
1981            if(typeof menu == "string"){ // menu id
1982                return menus[menu];
1983            }else if(menu.events){  // menu instance
1984                return menu;
1985            }
1986            /*else if(typeof menu.length == 'number'){ // array of menu items?
1987                return new Roo.bootstrap.Menu({items:menu});
1988            }else{ // otherwise, must be a config
1989                return new Roo.bootstrap.Menu(menu);
1990            }
1991            */
1992            return false;
1993        },
1994
1995        // private
1996        unregister : function(menu){
1997            delete menus[menu.id];
1998            menu.un("beforehide", onBeforeHide);
1999            menu.un("hide", onHide);
2000            menu.un("beforeshow", onBeforeShow);
2001            menu.un("show", onShow);
2002            var g = menu.group;
2003            if(g && menu.events["checkchange"]){
2004                groups[g].remove(menu);
2005                menu.un("checkchange", onCheck);
2006            }
2007        },
2008
2009        // private
2010        registerCheckable : function(menuItem){
2011            var g = menuItem.group;
2012            if(g){
2013                if(!groups[g]){
2014                    groups[g] = [];
2015                }
2016                groups[g].push(menuItem);
2017                menuItem.on("beforecheckchange", onBeforeCheck);
2018            }
2019        },
2020
2021        // private
2022        unregisterCheckable : function(menuItem){
2023            var g = menuItem.group;
2024            if(g){
2025                groups[g].remove(menuItem);
2026                menuItem.un("beforecheckchange", onBeforeCheck);
2027            }
2028        }
2029    };
2030 }();/*
2031  * - LGPL
2032  *
2033  * menu
2034  * 
2035  */
2036
2037 /**
2038  * @class Roo.bootstrap.Menu
2039  * @extends Roo.bootstrap.Component
2040  * Bootstrap Menu class - container for MenuItems
2041  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2043  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2044  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2045  * 
2046  * @constructor
2047  * Create a new Menu
2048  * @param {Object} config The config object
2049  */
2050
2051
2052 Roo.bootstrap.Menu = function(config){
2053     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054     if (this.registerMenu && this.type != 'treeview')  {
2055         Roo.bootstrap.MenuMgr.register(this);
2056     }
2057     
2058     
2059     this.addEvents({
2060         /**
2061          * @event beforeshow
2062          * Fires before this menu is displayed
2063          * @param {Roo.menu.Menu} this
2064          */
2065         beforeshow : true,
2066         /**
2067          * @event beforehide
2068          * Fires before this menu is hidden
2069          * @param {Roo.menu.Menu} this
2070          */
2071         beforehide : true,
2072         /**
2073          * @event show
2074          * Fires after this menu is displayed
2075          * @param {Roo.menu.Menu} this
2076          */
2077         show : true,
2078         /**
2079          * @event hide
2080          * Fires after this menu is hidden
2081          * @param {Roo.menu.Menu} this
2082          */
2083         hide : true,
2084         /**
2085          * @event click
2086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087          * @param {Roo.menu.Menu} this
2088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089          * @param {Roo.EventObject} e
2090          */
2091         click : true,
2092         /**
2093          * @event mouseover
2094          * Fires when the mouse is hovering over this menu
2095          * @param {Roo.menu.Menu} this
2096          * @param {Roo.EventObject} e
2097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2098          */
2099         mouseover : true,
2100         /**
2101          * @event mouseout
2102          * Fires when the mouse exits this menu
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.EventObject} e
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          */
2107         mouseout : true,
2108         /**
2109          * @event itemclick
2110          * Fires when a menu item contained in this menu is clicked
2111          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112          * @param {Roo.EventObject} e
2113          */
2114         itemclick: true
2115     });
2116     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2120     
2121    /// html : false,
2122     //align : '',
2123     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2124     type: false,
2125     /**
2126      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2127      */
2128     registerMenu : true,
2129     
2130     menuItems :false, // stores the menu items..
2131     
2132     hidden:true,
2133         
2134     parentMenu : false,
2135     
2136     stopEvent : true,
2137     
2138     isLink : false,
2139     
2140     getChildContainer : function() {
2141         return this.el;  
2142     },
2143     
2144     getAutoCreate : function(){
2145          
2146         //if (['right'].indexOf(this.align)!==-1) {
2147         //    cfg.cn[1].cls += ' pull-right'
2148         //}
2149         
2150         
2151         var cfg = {
2152             tag : 'ul',
2153             cls : 'dropdown-menu' ,
2154             style : 'z-index:1000'
2155             
2156         };
2157         
2158         if (this.type === 'submenu') {
2159             cfg.cls = 'submenu active';
2160         }
2161         if (this.type === 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         return cfg;
2166     },
2167     initEvents : function() {
2168         
2169        // Roo.log("ADD event");
2170        // Roo.log(this.triggerEl.dom);
2171         
2172         this.triggerEl.on('click', this.onTriggerClick, this);
2173         
2174         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2175         
2176         
2177         if (this.triggerEl.hasClass('nav-item')) {
2178             // dropdown toggle on the 'a' in BS4?
2179             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2180         } else {
2181             this.triggerEl.addClass('dropdown-toggle');
2182         }
2183         if (Roo.isTouch) {
2184             this.el.on('touchstart'  , this.onTouch, this);
2185         }
2186         this.el.on('click' , this.onClick, this);
2187
2188         this.el.on("mouseover", this.onMouseOver, this);
2189         this.el.on("mouseout", this.onMouseOut, this);
2190         
2191     },
2192     
2193     findTargetItem : function(e)
2194     {
2195         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2196         if(!t){
2197             return false;
2198         }
2199         //Roo.log(t);         Roo.log(t.id);
2200         if(t && t.id){
2201             //Roo.log(this.menuitems);
2202             return this.menuitems.get(t.id);
2203             
2204             //return this.items.get(t.menuItemId);
2205         }
2206         
2207         return false;
2208     },
2209     
2210     onTouch : function(e) 
2211     {
2212         Roo.log("menu.onTouch");
2213         //e.stopEvent(); this make the user popdown broken
2214         this.onClick(e);
2215     },
2216     
2217     onClick : function(e)
2218     {
2219         Roo.log("menu.onClick");
2220         
2221         var t = this.findTargetItem(e);
2222         if(!t || t.isContainer){
2223             return;
2224         }
2225         Roo.log(e);
2226         /*
2227         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2228             if(t == this.activeItem && t.shouldDeactivate(e)){
2229                 this.activeItem.deactivate();
2230                 delete this.activeItem;
2231                 return;
2232             }
2233             if(t.canActivate){
2234                 this.setActiveItem(t, true);
2235             }
2236             return;
2237             
2238             
2239         }
2240         */
2241        
2242         Roo.log('pass click event');
2243         
2244         t.onClick(e);
2245         
2246         this.fireEvent("click", this, t, e);
2247         
2248         var _this = this;
2249         
2250         if(!t.href.length || t.href == '#'){
2251             (function() { _this.hide(); }).defer(100);
2252         }
2253         
2254     },
2255     
2256     onMouseOver : function(e){
2257         var t  = this.findTargetItem(e);
2258         //Roo.log(t);
2259         //if(t){
2260         //    if(t.canActivate && !t.disabled){
2261         //        this.setActiveItem(t, true);
2262         //    }
2263         //}
2264         
2265         this.fireEvent("mouseover", this, e, t);
2266     },
2267     isVisible : function(){
2268         return !this.hidden;
2269     },
2270      onMouseOut : function(e){
2271         var t  = this.findTargetItem(e);
2272         
2273         //if(t ){
2274         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2275         //        this.activeItem.deactivate();
2276         //        delete this.activeItem;
2277         //    }
2278         //}
2279         this.fireEvent("mouseout", this, e, t);
2280     },
2281     
2282     
2283     /**
2284      * Displays this menu relative to another element
2285      * @param {String/HTMLElement/Roo.Element} element The element to align to
2286      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287      * the element (defaults to this.defaultAlign)
2288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2289      */
2290     show : function(el, pos, parentMenu){
2291         this.parentMenu = parentMenu;
2292         if(!this.el){
2293             this.render();
2294         }
2295         this.fireEvent("beforeshow", this);
2296         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2297     },
2298      /**
2299      * Displays this menu at a specific xy position
2300      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2302      */
2303     showAt : function(xy, parentMenu, /* private: */_e){
2304         this.parentMenu = parentMenu;
2305         if(!this.el){
2306             this.render();
2307         }
2308         if(_e !== false){
2309             this.fireEvent("beforeshow", this);
2310             //xy = this.el.adjustForConstraints(xy);
2311         }
2312         
2313         //this.el.show();
2314         this.hideMenuItems();
2315         this.hidden = false;
2316         this.triggerEl.addClass('open');
2317         this.el.addClass('show');
2318         
2319         // reassign x when hitting right
2320         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2322         }
2323         
2324         // reassign y when hitting bottom
2325         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2327         }
2328         
2329         // but the list may align on trigger left or trigger top... should it be a properity?
2330         
2331         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2332             this.el.setXY(xy);
2333         }
2334         
2335         this.focus();
2336         this.fireEvent("show", this);
2337     },
2338     
2339     focus : function(){
2340         return;
2341         if(!this.hidden){
2342             this.doFocus.defer(50, this);
2343         }
2344     },
2345
2346     doFocus : function(){
2347         if(!this.hidden){
2348             this.focusEl.focus();
2349         }
2350     },
2351
2352     /**
2353      * Hides this menu and optionally all parent menus
2354      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2355      */
2356     hide : function(deep)
2357     {
2358         
2359         this.hideMenuItems();
2360         if(this.el && this.isVisible()){
2361             this.fireEvent("beforehide", this);
2362             if(this.activeItem){
2363                 this.activeItem.deactivate();
2364                 this.activeItem = null;
2365             }
2366             this.triggerEl.removeClass('open');;
2367             this.el.removeClass('show');
2368             this.hidden = true;
2369             this.fireEvent("hide", this);
2370         }
2371         if(deep === true && this.parentMenu){
2372             this.parentMenu.hide(true);
2373         }
2374     },
2375     
2376     onTriggerClick : function(e)
2377     {
2378         Roo.log('trigger click');
2379         
2380         var target = e.getTarget();
2381         
2382         Roo.log(target.nodeName.toLowerCase());
2383         
2384         if(target.nodeName.toLowerCase() === 'i'){
2385             e.preventDefault();
2386         }
2387         
2388     },
2389     
2390     onTriggerPress  : function(e)
2391     {
2392         Roo.log('trigger press');
2393         //Roo.log(e.getTarget());
2394        // Roo.log(this.triggerEl.dom);
2395        
2396         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397         var pel = Roo.get(e.getTarget());
2398         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399             Roo.log('is treeview or dropdown?');
2400             return;
2401         }
2402         
2403         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2404             return;
2405         }
2406         
2407         if (this.isVisible()) {
2408             Roo.log('hide');
2409             this.hide();
2410         } else {
2411             Roo.log('show');
2412             this.show(this.triggerEl, false, false);
2413         }
2414         
2415         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2416             e.stopEvent();
2417         }
2418         
2419     },
2420        
2421     
2422     hideMenuItems : function()
2423     {
2424         Roo.log("hide Menu Items");
2425         if (!this.el) { 
2426             return;
2427         }
2428         //$(backdrop).remove()
2429         this.el.select('.open',true).each(function(aa) {
2430             
2431             aa.removeClass('open');
2432           //var parent = getParent($(this))
2433           //var relatedTarget = { relatedTarget: this }
2434           
2435            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436           //if (e.isDefaultPrevented()) return
2437            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2438         });
2439     },
2440     addxtypeChild : function (tree, cntr) {
2441         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2442           
2443         this.menuitems.add(comp);
2444         return comp;
2445
2446     },
2447     getEl : function()
2448     {
2449         Roo.log(this.el);
2450         return this.el;
2451     },
2452     
2453     clear : function()
2454     {
2455         this.getEl().dom.innerHTML = '';
2456         this.menuitems.clear();
2457     }
2458 });
2459
2460  
2461  /*
2462  * - LGPL
2463  *
2464  * menu item
2465  * 
2466  */
2467
2468
2469 /**
2470  * @class Roo.bootstrap.MenuItem
2471  * @extends Roo.bootstrap.Component
2472  * Bootstrap MenuItem class
2473  * @cfg {String} html the menu label
2474  * @cfg {String} href the link
2475  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2478  * @cfg {String} fa favicon to show on left of menu item.
2479  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2480  * 
2481  * 
2482  * @constructor
2483  * Create a new MenuItem
2484  * @param {Object} config The config object
2485  */
2486
2487
2488 Roo.bootstrap.MenuItem = function(config){
2489     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2490     this.addEvents({
2491         // raw events
2492         /**
2493          * @event click
2494          * The raw click event for the entire grid.
2495          * @param {Roo.bootstrap.MenuItem} this
2496          * @param {Roo.EventObject} e
2497          */
2498         "click" : true
2499     });
2500 };
2501
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2503     
2504     href : false,
2505     html : false,
2506     preventDefault: false,
2507     isContainer : false,
2508     active : false,
2509     fa: false,
2510     
2511     getAutoCreate : function(){
2512         
2513         if(this.isContainer){
2514             return {
2515                 tag: 'li',
2516                 cls: 'dropdown-menu-item dropdown-item'
2517             };
2518         }
2519         var ctag = {
2520             tag: 'span',
2521             html: 'Link'
2522         };
2523         
2524         var anc = {
2525             tag : 'a',
2526             href : '#',
2527             cn : [  ]
2528         };
2529         
2530         if (this.fa !== false) {
2531             anc.cn.push({
2532                 tag : 'i',
2533                 cls : 'fa fa-' + this.fa
2534             });
2535         }
2536         
2537         anc.cn.push(ctag);
2538         
2539         
2540         var cfg= {
2541             tag: 'li',
2542             cls: 'dropdown-menu-item dropdown-item',
2543             cn: [ anc ]
2544         };
2545         if (this.parent().type == 'treeview') {
2546             cfg.cls = 'treeview-menu';
2547         }
2548         if (this.active) {
2549             cfg.cls += ' active';
2550         }
2551         
2552         
2553         
2554         anc.href = this.href || cfg.cn[0].href ;
2555         ctag.html = this.html || cfg.cn[0].html ;
2556         return cfg;
2557     },
2558     
2559     initEvents: function()
2560     {
2561         if (this.parent().type == 'treeview') {
2562             this.el.select('a').on('click', this.onClick, this);
2563         }
2564         
2565         if (this.menu) {
2566             this.menu.parentType = this.xtype;
2567             this.menu.triggerEl = this.el;
2568             this.menu = this.addxtype(Roo.apply({}, this.menu));
2569         }
2570         
2571     },
2572     onClick : function(e)
2573     {
2574         Roo.log('item on click ');
2575         
2576         if(this.preventDefault){
2577             e.preventDefault();
2578         }
2579         //this.parent().hideMenuItems();
2580         
2581         this.fireEvent('click', this, e);
2582     },
2583     getEl : function()
2584     {
2585         return this.el;
2586     } 
2587 });
2588
2589  
2590
2591  /*
2592  * - LGPL
2593  *
2594  * menu separator
2595  * 
2596  */
2597
2598
2599 /**
2600  * @class Roo.bootstrap.MenuSeparator
2601  * @extends Roo.bootstrap.Component
2602  * Bootstrap MenuSeparator class
2603  * 
2604  * @constructor
2605  * Create a new MenuItem
2606  * @param {Object} config The config object
2607  */
2608
2609
2610 Roo.bootstrap.MenuSeparator = function(config){
2611     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2612 };
2613
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2615     
2616     getAutoCreate : function(){
2617         var cfg = {
2618             cls: 'divider',
2619             tag : 'li'
2620         };
2621         
2622         return cfg;
2623     }
2624    
2625 });
2626
2627  
2628
2629  
2630 /*
2631 * Licence: LGPL
2632 */
2633
2634 /**
2635  * @class Roo.bootstrap.Modal
2636  * @extends Roo.bootstrap.Component
2637  * Bootstrap Modal class
2638  * @cfg {String} title Title of dialog
2639  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2641  * @cfg {Boolean} specificTitle default false
2642  * @cfg {Array} buttons Array of buttons or standard button set..
2643  * @cfg {String} buttonPosition (left|right|center) default right
2644  * @cfg {Boolean} animate default true
2645  * @cfg {Boolean} allow_close default true
2646  * @cfg {Boolean} fitwindow default false
2647  * @cfg {String} size (sm|lg) default empty
2648  * @cfg {Number} max_width set the max width of modal
2649  *
2650  *
2651  * @constructor
2652  * Create a new Modal Dialog
2653  * @param {Object} config The config object
2654  */
2655
2656 Roo.bootstrap.Modal = function(config){
2657     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2658     this.addEvents({
2659         // raw events
2660         /**
2661          * @event btnclick
2662          * The raw btnclick event for the button
2663          * @param {Roo.EventObject} e
2664          */
2665         "btnclick" : true,
2666         /**
2667          * @event resize
2668          * Fire when dialog resize
2669          * @param {Roo.bootstrap.Modal} this
2670          * @param {Roo.EventObject} e
2671          */
2672         "resize" : true
2673     });
2674     this.buttons = this.buttons || [];
2675
2676     if (this.tmpl) {
2677         this.tmpl = Roo.factory(this.tmpl);
2678     }
2679
2680 };
2681
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2683
2684     title : 'test dialog',
2685
2686     buttons : false,
2687
2688     // set on load...
2689
2690     html: false,
2691
2692     tmp: false,
2693
2694     specificTitle: false,
2695
2696     buttonPosition: 'right',
2697
2698     allow_close : true,
2699
2700     animate : true,
2701
2702     fitwindow: false,
2703     
2704      // private
2705     dialogEl: false,
2706     bodyEl:  false,
2707     footerEl:  false,
2708     titleEl:  false,
2709     closeEl:  false,
2710
2711     size: '',
2712     
2713     max_width: 0,
2714     
2715     max_height: 0,
2716     
2717     fit_content: false,
2718
2719     onRender : function(ct, position)
2720     {
2721         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2722
2723         if(!this.el){
2724             var cfg = Roo.apply({},  this.getAutoCreate());
2725             cfg.id = Roo.id();
2726             //if(!cfg.name){
2727             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2728             //}
2729             //if (!cfg.name.length) {
2730             //    delete cfg.name;
2731            // }
2732             if (this.cls) {
2733                 cfg.cls += ' ' + this.cls;
2734             }
2735             if (this.style) {
2736                 cfg.style = this.style;
2737             }
2738             this.el = Roo.get(document.body).createChild(cfg, position);
2739         }
2740         //var type = this.el.dom.type;
2741
2742
2743         if(this.tabIndex !== undefined){
2744             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2745         }
2746
2747         this.dialogEl = this.el.select('.modal-dialog',true).first();
2748         this.bodyEl = this.el.select('.modal-body',true).first();
2749         this.closeEl = this.el.select('.modal-header .close', true).first();
2750         this.headerEl = this.el.select('.modal-header',true).first();
2751         this.titleEl = this.el.select('.modal-title',true).first();
2752         this.footerEl = this.el.select('.modal-footer',true).first();
2753
2754         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2755         
2756         //this.el.addClass("x-dlg-modal");
2757
2758         if (this.buttons.length) {
2759             Roo.each(this.buttons, function(bb) {
2760                 var b = Roo.apply({}, bb);
2761                 b.xns = b.xns || Roo.bootstrap;
2762                 b.xtype = b.xtype || 'Button';
2763                 if (typeof(b.listeners) == 'undefined') {
2764                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2765                 }
2766
2767                 var btn = Roo.factory(b);
2768
2769                 btn.render(this.el.select('.modal-footer div').first());
2770
2771             },this);
2772         }
2773         // render the children.
2774         var nitems = [];
2775
2776         if(typeof(this.items) != 'undefined'){
2777             var items = this.items;
2778             delete this.items;
2779
2780             for(var i =0;i < items.length;i++) {
2781                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2782             }
2783         }
2784
2785         this.items = nitems;
2786
2787         // where are these used - they used to be body/close/footer
2788
2789
2790         this.initEvents();
2791         //this.el.addClass([this.fieldClass, this.cls]);
2792
2793     },
2794
2795     getAutoCreate : function()
2796     {
2797         var bdy = {
2798                 cls : 'modal-body',
2799                 html : this.html || ''
2800         };
2801
2802         var title = {
2803             tag: 'h4',
2804             cls : 'modal-title',
2805             html : this.title
2806         };
2807
2808         if(this.specificTitle){
2809             title = this.title;
2810
2811         };
2812
2813         var header = [];
2814         if (this.allow_close && Roo.bootstrap.version == 3) {
2815             header.push({
2816                 tag: 'button',
2817                 cls : 'close',
2818                 html : '&times'
2819             });
2820         }
2821
2822         header.push(title);
2823
2824         if (this.allow_close && Roo.bootstrap.version == 4) {
2825             header.push({
2826                 tag: 'button',
2827                 cls : 'close',
2828                 html : '&times'
2829             });
2830         }
2831         
2832         var size = '';
2833
2834         if(this.size.length){
2835             size = 'modal-' + this.size;
2836         }
2837
2838         var modal = {
2839             cls: "modal",
2840              cn : [
2841                 {
2842                     cls: "modal-dialog " + size,
2843                     cn : [
2844                         {
2845                             cls : "modal-content",
2846                             cn : [
2847                                 {
2848                                     cls : 'modal-header',
2849                                     cn : header
2850                                 },
2851                                 bdy,
2852                                 {
2853                                     cls : 'modal-footer',
2854                                     cn : [
2855                                         {
2856                                             tag: 'div',
2857                                             cls: 'btn-' + this.buttonPosition
2858                                         }
2859                                     ]
2860
2861                                 }
2862
2863
2864                             ]
2865
2866                         }
2867                     ]
2868
2869                 }
2870             ]
2871         };
2872
2873         if(this.animate){
2874             modal.cls += ' fade';
2875         }
2876
2877         return modal;
2878
2879     },
2880     getChildContainer : function() {
2881
2882          return this.bodyEl;
2883
2884     },
2885     getButtonContainer : function() {
2886          return this.el.select('.modal-footer div',true).first();
2887
2888     },
2889     initEvents : function()
2890     {
2891         if (this.allow_close) {
2892             this.closeEl.on('click', this.hide, this);
2893         }
2894         Roo.EventManager.onWindowResize(this.resize, this, true);
2895
2896
2897     },
2898
2899     resize : function()
2900     {
2901         this.maskEl.setSize(
2902             Roo.lib.Dom.getViewWidth(true),
2903             Roo.lib.Dom.getViewHeight(true)
2904         );
2905         
2906         if (this.fitwindow) {
2907             this.setSize(
2908                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2910             );
2911             return;
2912         }
2913         
2914         if(this.max_width !== 0) {
2915             
2916             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2917             
2918             if(this.height) {
2919                 this.setSize(w, this.height);
2920                 return;
2921             }
2922             
2923             if(this.max_height) {
2924                 this.setSize(w,Math.min(
2925                     this.max_height,
2926                     Roo.lib.Dom.getViewportHeight(true) - 60
2927                 ));
2928                 
2929                 return;
2930             }
2931             
2932             if(!this.fit_content) {
2933                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2934                 return;
2935             }
2936             
2937             this.setSize(w, Math.min(
2938                 60 +
2939                 this.headerEl.getHeight() + 
2940                 this.footerEl.getHeight() + 
2941                 this.getChildHeight(this.bodyEl.dom.childNodes),
2942                 Roo.lib.Dom.getViewportHeight(true) - 60)
2943             );
2944         }
2945         
2946     },
2947
2948     setSize : function(w,h)
2949     {
2950         if (!w && !h) {
2951             return;
2952         }
2953         
2954         this.resizeTo(w,h);
2955     },
2956
2957     show : function() {
2958
2959         if (!this.rendered) {
2960             this.render();
2961         }
2962
2963         //this.el.setStyle('display', 'block');
2964         this.el.removeClass('hideing');
2965         this.el.dom.style.display='block';
2966         
2967         Roo.get(document.body).addClass('modal-open');
2968  
2969         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2970             var _this = this;
2971             (function(){
2972                 this.el.addClass('show');
2973                 this.el.addClass('in');
2974             }).defer(50, this);
2975         }else{
2976             this.el.addClass('show');
2977             this.el.addClass('in');
2978         }
2979
2980         // not sure how we can show data in here..
2981         //if (this.tmpl) {
2982         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2983         //}
2984
2985         Roo.get(document.body).addClass("x-body-masked");
2986         
2987         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2988         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989         this.maskEl.dom.style.display = 'block';
2990         this.maskEl.addClass('show');
2991         
2992         
2993         this.resize();
2994         
2995         this.fireEvent('show', this);
2996
2997         // set zindex here - otherwise it appears to be ignored...
2998         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2999
3000         (function () {
3001             this.items.forEach( function(e) {
3002                 e.layout ? e.layout() : false;
3003
3004             });
3005         }).defer(100,this);
3006
3007     },
3008     hide : function()
3009     {
3010         if(this.fireEvent("beforehide", this) !== false){
3011             
3012             this.maskEl.removeClass('show');
3013             
3014             this.maskEl.dom.style.display = '';
3015             Roo.get(document.body).removeClass("x-body-masked");
3016             this.el.removeClass('in');
3017             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3018
3019             if(this.animate){ // why
3020                 this.el.addClass('hideing');
3021                 this.el.removeClass('show');
3022                 (function(){
3023                     if (!this.el.hasClass('hideing')) {
3024                         return; // it's been shown again...
3025                     }
3026                     
3027                     this.el.dom.style.display='';
3028
3029                     Roo.get(document.body).removeClass('modal-open');
3030                     this.el.removeClass('hideing');
3031                 }).defer(150,this);
3032                 
3033             }else{
3034                 this.el.removeClass('show');
3035                 this.el.dom.style.display='';
3036                 Roo.get(document.body).removeClass('modal-open');
3037
3038             }
3039             this.fireEvent('hide', this);
3040         }
3041     },
3042     isVisible : function()
3043     {
3044         
3045         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3046         
3047     },
3048
3049     addButton : function(str, cb)
3050     {
3051
3052
3053         var b = Roo.apply({}, { html : str } );
3054         b.xns = b.xns || Roo.bootstrap;
3055         b.xtype = b.xtype || 'Button';
3056         if (typeof(b.listeners) == 'undefined') {
3057             b.listeners = { click : cb.createDelegate(this)  };
3058         }
3059
3060         var btn = Roo.factory(b);
3061
3062         btn.render(this.el.select('.modal-footer div').first());
3063
3064         return btn;
3065
3066     },
3067
3068     setDefaultButton : function(btn)
3069     {
3070         //this.el.select('.modal-footer').()
3071     },
3072     diff : false,
3073
3074     resizeTo: function(w,h)
3075     {
3076         // skip.. ?? why??
3077
3078         this.dialogEl.setWidth(w);
3079         if (this.diff === false) {
3080             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3081         }
3082
3083         this.bodyEl.setHeight(h - this.diff);
3084
3085         this.fireEvent('resize', this);
3086
3087     },
3088     setContentSize  : function(w, h)
3089     {
3090
3091     },
3092     onButtonClick: function(btn,e)
3093     {
3094         //Roo.log([a,b,c]);
3095         this.fireEvent('btnclick', btn.name, e);
3096     },
3097      /**
3098      * Set the title of the Dialog
3099      * @param {String} str new Title
3100      */
3101     setTitle: function(str) {
3102         this.titleEl.dom.innerHTML = str;
3103     },
3104     /**
3105      * Set the body of the Dialog
3106      * @param {String} str new Title
3107      */
3108     setBody: function(str) {
3109         this.bodyEl.dom.innerHTML = str;
3110     },
3111     /**
3112      * Set the body of the Dialog using the template
3113      * @param {Obj} data - apply this data to the template and replace the body contents.
3114      */
3115     applyBody: function(obj)
3116     {
3117         if (!this.tmpl) {
3118             Roo.log("Error - using apply Body without a template");
3119             //code
3120         }
3121         this.tmpl.overwrite(this.bodyEl, obj);
3122     },
3123     
3124     getChildHeight : function(child_nodes)
3125     {
3126         if(
3127             !child_nodes ||
3128             child_nodes.length == 0
3129         ) {
3130             return;
3131         }
3132         
3133         var child_height = 0;
3134         
3135         for(var i = 0; i < child_nodes.length; i++) {
3136             
3137             /*
3138             * for modal with tabs...
3139             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3140                 
3141                 var layout_childs = child_nodes[i].childNodes;
3142                 
3143                 for(var j = 0; j < layout_childs.length; j++) {
3144                     
3145                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3146                         
3147                         var layout_body_childs = layout_childs[j].childNodes;
3148                         
3149                         for(var k = 0; k < layout_body_childs.length; k++) {
3150                             
3151                             if(layout_body_childs[k].classList.contains('navbar')) {
3152                                 child_height += layout_body_childs[k].offsetHeight;
3153                                 continue;
3154                             }
3155                             
3156                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3157                                 
3158                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3159                                 
3160                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3161                                     
3162                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3164                                         continue;
3165                                     }
3166                                     
3167                                 }
3168                                 
3169                             }
3170                             
3171                         }
3172                     }
3173                 }
3174                 continue;
3175             }
3176             */
3177             
3178             child_height += child_nodes[i].offsetHeight;
3179             // Roo.log(child_nodes[i].offsetHeight);
3180         }
3181         
3182         return child_height;
3183     }
3184
3185 });
3186
3187
3188 Roo.apply(Roo.bootstrap.Modal,  {
3189     /**
3190          * Button config that displays a single OK button
3191          * @type Object
3192          */
3193         OK :  [{
3194             name : 'ok',
3195             weight : 'primary',
3196             html : 'OK'
3197         }],
3198         /**
3199          * Button config that displays Yes and No buttons
3200          * @type Object
3201          */
3202         YESNO : [
3203             {
3204                 name  : 'no',
3205                 html : 'No'
3206             },
3207             {
3208                 name  :'yes',
3209                 weight : 'primary',
3210                 html : 'Yes'
3211             }
3212         ],
3213
3214         /**
3215          * Button config that displays OK and Cancel buttons
3216          * @type Object
3217          */
3218         OKCANCEL : [
3219             {
3220                name : 'cancel',
3221                 html : 'Cancel'
3222             },
3223             {
3224                 name : 'ok',
3225                 weight : 'primary',
3226                 html : 'OK'
3227             }
3228         ],
3229         /**
3230          * Button config that displays Yes, No and Cancel buttons
3231          * @type Object
3232          */
3233         YESNOCANCEL : [
3234             {
3235                 name : 'yes',
3236                 weight : 'primary',
3237                 html : 'Yes'
3238             },
3239             {
3240                 name : 'no',
3241                 html : 'No'
3242             },
3243             {
3244                 name : 'cancel',
3245                 html : 'Cancel'
3246             }
3247         ],
3248         
3249         zIndex : 10001
3250 });
3251 /*
3252  * - LGPL
3253  *
3254  * messagebox - can be used as a replace
3255  * 
3256  */
3257 /**
3258  * @class Roo.MessageBox
3259  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3260  * Example usage:
3261  *<pre><code>
3262 // Basic alert:
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3264
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3267     if (btn == 'ok'){
3268         // process text value...
3269     }
3270 });
3271
3272 // Show a dialog using config options:
3273 Roo.Msg.show({
3274    title:'Save Changes?',
3275    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276    buttons: Roo.Msg.YESNOCANCEL,
3277    fn: processResult,
3278    animEl: 'elId'
3279 });
3280 </code></pre>
3281  * @singleton
3282  */
3283 Roo.bootstrap.MessageBox = function(){
3284     var dlg, opt, mask, waitTimer;
3285     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286     var buttons, activeTextEl, bwidth;
3287
3288     
3289     // private
3290     var handleButton = function(button){
3291         dlg.hide();
3292         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3293     };
3294
3295     // private
3296     var handleHide = function(){
3297         if(opt && opt.cls){
3298             dlg.el.removeClass(opt.cls);
3299         }
3300         //if(waitTimer){
3301         //    Roo.TaskMgr.stop(waitTimer);
3302         //    waitTimer = null;
3303         //}
3304     };
3305
3306     // private
3307     var updateButtons = function(b){
3308         var width = 0;
3309         if(!b){
3310             buttons["ok"].hide();
3311             buttons["cancel"].hide();
3312             buttons["yes"].hide();
3313             buttons["no"].hide();
3314             //dlg.footer.dom.style.display = 'none';
3315             return width;
3316         }
3317         dlg.footerEl.dom.style.display = '';
3318         for(var k in buttons){
3319             if(typeof buttons[k] != "function"){
3320                 if(b[k]){
3321                     buttons[k].show();
3322                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323                     width += buttons[k].el.getWidth()+15;
3324                 }else{
3325                     buttons[k].hide();
3326                 }
3327             }
3328         }
3329         return width;
3330     };
3331
3332     // private
3333     var handleEsc = function(d, k, e){
3334         if(opt && opt.closable !== false){
3335             dlg.hide();
3336         }
3337         if(e){
3338             e.stopEvent();
3339         }
3340     };
3341
3342     return {
3343         /**
3344          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345          * @return {Roo.BasicDialog} The BasicDialog element
3346          */
3347         getDialog : function(){
3348            if(!dlg){
3349                 dlg = new Roo.bootstrap.Modal( {
3350                     //draggable: true,
3351                     //resizable:false,
3352                     //constraintoviewport:false,
3353                     //fixedcenter:true,
3354                     //collapsible : false,
3355                     //shim:true,
3356                     //modal: true,
3357                 //    width: 'auto',
3358                   //  height:100,
3359                     //buttonAlign:"center",
3360                     closeClick : function(){
3361                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3362                             handleButton("no");
3363                         }else{
3364                             handleButton("cancel");
3365                         }
3366                     }
3367                 });
3368                 dlg.render();
3369                 dlg.on("hide", handleHide);
3370                 mask = dlg.mask;
3371                 //dlg.addKeyListener(27, handleEsc);
3372                 buttons = {};
3373                 this.buttons = buttons;
3374                 var bt = this.buttonText;
3375                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3379                 //Roo.log(buttons);
3380                 bodyEl = dlg.bodyEl.createChild({
3381
3382                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383                         '<textarea class="roo-mb-textarea"></textarea>' +
3384                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3385                 });
3386                 msgEl = bodyEl.dom.firstChild;
3387                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388                 textboxEl.enableDisplayMode();
3389                 textboxEl.addKeyListener([10,13], function(){
3390                     if(dlg.isVisible() && opt && opt.buttons){
3391                         if(opt.buttons.ok){
3392                             handleButton("ok");
3393                         }else if(opt.buttons.yes){
3394                             handleButton("yes");
3395                         }
3396                     }
3397                 });
3398                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399                 textareaEl.enableDisplayMode();
3400                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401                 progressEl.enableDisplayMode();
3402                 
3403                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404                 var pf = progressEl.dom.firstChild;
3405                 if (pf) {
3406                     pp = Roo.get(pf.firstChild);
3407                     pp.setHeight(pf.offsetHeight);
3408                 }
3409                 
3410             }
3411             return dlg;
3412         },
3413
3414         /**
3415          * Updates the message box body text
3416          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417          * the XHTML-compliant non-breaking space character '&amp;#160;')
3418          * @return {Roo.MessageBox} This message box
3419          */
3420         updateText : function(text)
3421         {
3422             if(!dlg.isVisible() && !opt.width){
3423                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3425             }
3426             msgEl.innerHTML = text || '&#160;';
3427       
3428             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3430             var w = Math.max(
3431                     Math.min(opt.width || cw , this.maxWidth), 
3432                     Math.max(opt.minWidth || this.minWidth, bwidth)
3433             );
3434             if(opt.prompt){
3435                 activeTextEl.setWidth(w);
3436             }
3437             if(dlg.isVisible()){
3438                 dlg.fixedcenter = false;
3439             }
3440             // to big, make it scroll. = But as usual stupid IE does not support
3441             // !important..
3442             
3443             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3446             } else {
3447                 bodyEl.dom.style.height = '';
3448                 bodyEl.dom.style.overflowY = '';
3449             }
3450             if (cw > w) {
3451                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3452             } else {
3453                 bodyEl.dom.style.overflowX = '';
3454             }
3455             
3456             dlg.setContentSize(w, bodyEl.getHeight());
3457             if(dlg.isVisible()){
3458                 dlg.fixedcenter = true;
3459             }
3460             return this;
3461         },
3462
3463         /**
3464          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3465          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468          * @return {Roo.MessageBox} This message box
3469          */
3470         updateProgress : function(value, text){
3471             if(text){
3472                 this.updateText(text);
3473             }
3474             
3475             if (pp) { // weird bug on my firefox - for some reason this is not defined
3476                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3478             }
3479             return this;
3480         },        
3481
3482         /**
3483          * Returns true if the message box is currently displayed
3484          * @return {Boolean} True if the message box is visible, else false
3485          */
3486         isVisible : function(){
3487             return dlg && dlg.isVisible();  
3488         },
3489
3490         /**
3491          * Hides the message box if it is displayed
3492          */
3493         hide : function(){
3494             if(this.isVisible()){
3495                 dlg.hide();
3496             }  
3497         },
3498
3499         /**
3500          * Displays a new message box, or reinitializes an existing message box, based on the config options
3501          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502          * The following config object properties are supported:
3503          * <pre>
3504 Property    Type             Description
3505 ----------  ---------------  ------------------------------------------------------------------------------------
3506 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3507                                    closes (defaults to undefined)
3508 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3511                                    progress and wait dialogs will ignore this property and always hide the
3512                                    close button as they can only be closed programmatically.
3513 cls               String           A custom CSS class to apply to the message box element
3514 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3515                                    displayed (defaults to 75)
3516 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3517                                    function will be btn (the name of the button that was clicked, if applicable,
3518                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3519                                    Progress and wait dialogs will ignore this option since they do not respond to
3520                                    user actions and can only be closed programmatically, so any required function
3521                                    should be called by the same code after it closes the dialog.
3522 icon              String           A CSS class that provides a background image to be used as an icon for
3523                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3525 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3526 modal             Boolean          False to allow user interaction with the page while the message box is
3527                                    displayed (defaults to true)
3528 msg               String           A string that will replace the existing message box body text (defaults
3529                                    to the XHTML-compliant non-breaking space character '&#160;')
3530 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3531 progress          Boolean          True to display a progress bar (defaults to false)
3532 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3535 title             String           The title text
3536 value             String           The string value to set into the active textbox element if displayed
3537 wait              Boolean          True to display a progress bar (defaults to false)
3538 width             Number           The width of the dialog in pixels
3539 </pre>
3540          *
3541          * Example usage:
3542          * <pre><code>
3543 Roo.Msg.show({
3544    title: 'Address',
3545    msg: 'Please enter your address:',
3546    width: 300,
3547    buttons: Roo.MessageBox.OKCANCEL,
3548    multiline: true,
3549    fn: saveAddress,
3550    animEl: 'addAddressBtn'
3551 });
3552 </code></pre>
3553          * @param {Object} config Configuration options
3554          * @return {Roo.MessageBox} This message box
3555          */
3556         show : function(options)
3557         {
3558             
3559             // this causes nightmares if you show one dialog after another
3560             // especially on callbacks..
3561              
3562             if(this.isVisible()){
3563                 
3564                 this.hide();
3565                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3567                 Roo.log("New Dialog Message:" +  options.msg )
3568                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3570                 
3571             }
3572             var d = this.getDialog();
3573             opt = options;
3574             d.setTitle(opt.title || "&#160;");
3575             d.closeEl.setDisplayed(opt.closable !== false);
3576             activeTextEl = textboxEl;
3577             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3578             if(opt.prompt){
3579                 if(opt.multiline){
3580                     textboxEl.hide();
3581                     textareaEl.show();
3582                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3583                         opt.multiline : this.defaultTextHeight);
3584                     activeTextEl = textareaEl;
3585                 }else{
3586                     textboxEl.show();
3587                     textareaEl.hide();
3588                 }
3589             }else{
3590                 textboxEl.hide();
3591                 textareaEl.hide();
3592             }
3593             progressEl.setDisplayed(opt.progress === true);
3594             this.updateProgress(0);
3595             activeTextEl.dom.value = opt.value || "";
3596             if(opt.prompt){
3597                 dlg.setDefaultButton(activeTextEl);
3598             }else{
3599                 var bs = opt.buttons;
3600                 var db = null;
3601                 if(bs && bs.ok){
3602                     db = buttons["ok"];
3603                 }else if(bs && bs.yes){
3604                     db = buttons["yes"];
3605                 }
3606                 dlg.setDefaultButton(db);
3607             }
3608             bwidth = updateButtons(opt.buttons);
3609             this.updateText(opt.msg);
3610             if(opt.cls){
3611                 d.el.addClass(opt.cls);
3612             }
3613             d.proxyDrag = opt.proxyDrag === true;
3614             d.modal = opt.modal !== false;
3615             d.mask = opt.modal !== false ? mask : false;
3616             if(!d.isVisible()){
3617                 // force it to the end of the z-index stack so it gets a cursor in FF
3618                 document.body.appendChild(dlg.el.dom);
3619                 d.animateTarget = null;
3620                 d.show(options.animEl);
3621             }
3622             return this;
3623         },
3624
3625         /**
3626          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3627          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628          * and closing the message box when the process is complete.
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @return {Roo.MessageBox} This message box
3632          */
3633         progress : function(title, msg){
3634             this.show({
3635                 title : title,
3636                 msg : msg,
3637                 buttons: false,
3638                 progress:true,
3639                 closable:false,
3640                 minWidth: this.minProgressWidth,
3641                 modal : true
3642             });
3643             return this;
3644         },
3645
3646         /**
3647          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648          * If a callback function is passed it will be called after the user clicks the button, and the
3649          * id of the button that was clicked will be passed as the only parameter to the callback
3650          * (could also be the top-right close button).
3651          * @param {String} title The title bar text
3652          * @param {String} msg The message box body text
3653          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654          * @param {Object} scope (optional) The scope of the callback function
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         alert : function(title, msg, fn, scope)
3658         {
3659             this.show({
3660                 title : title,
3661                 msg : msg,
3662                 buttons: this.OK,
3663                 fn: fn,
3664                 closable : false,
3665                 scope : scope,
3666                 modal : true
3667             });
3668             return this;
3669         },
3670
3671         /**
3672          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3673          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674          * You are responsible for closing the message box when the process is complete.
3675          * @param {String} msg The message box body text
3676          * @param {String} title (optional) The title bar text
3677          * @return {Roo.MessageBox} This message box
3678          */
3679         wait : function(msg, title){
3680             this.show({
3681                 title : title,
3682                 msg : msg,
3683                 buttons: false,
3684                 closable:false,
3685                 progress:true,
3686                 modal:true,
3687                 width:300,
3688                 wait:true
3689             });
3690             waitTimer = Roo.TaskMgr.start({
3691                 run: function(i){
3692                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3693                 },
3694                 interval: 1000
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703          * @param {String} title The title bar text
3704          * @param {String} msg The message box body text
3705          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706          * @param {Object} scope (optional) The scope of the callback function
3707          * @return {Roo.MessageBox} This message box
3708          */
3709         confirm : function(title, msg, fn, scope){
3710             this.show({
3711                 title : title,
3712                 msg : msg,
3713                 buttons: this.YESNO,
3714                 fn: fn,
3715                 scope : scope,
3716                 modal : true
3717             });
3718             return this;
3719         },
3720
3721         /**
3722          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3724          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725          * (could also be the top-right close button) and the text that was entered will be passed as the two
3726          * parameters to the callback.
3727          * @param {String} title The title bar text
3728          * @param {String} msg The message box body text
3729          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730          * @param {Object} scope (optional) The scope of the callback function
3731          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733          * @return {Roo.MessageBox} This message box
3734          */
3735         prompt : function(title, msg, fn, scope, multiline){
3736             this.show({
3737                 title : title,
3738                 msg : msg,
3739                 buttons: this.OKCANCEL,
3740                 fn: fn,
3741                 minWidth:250,
3742                 scope : scope,
3743                 prompt:true,
3744                 multiline: multiline,
3745                 modal : true
3746             });
3747             return this;
3748         },
3749
3750         /**
3751          * Button config that displays a single OK button
3752          * @type Object
3753          */
3754         OK : {ok:true},
3755         /**
3756          * Button config that displays Yes and No buttons
3757          * @type Object
3758          */
3759         YESNO : {yes:true, no:true},
3760         /**
3761          * Button config that displays OK and Cancel buttons
3762          * @type Object
3763          */
3764         OKCANCEL : {ok:true, cancel:true},
3765         /**
3766          * Button config that displays Yes, No and Cancel buttons
3767          * @type Object
3768          */
3769         YESNOCANCEL : {yes:true, no:true, cancel:true},
3770
3771         /**
3772          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3773          * @type Number
3774          */
3775         defaultTextHeight : 75,
3776         /**
3777          * The maximum width in pixels of the message box (defaults to 600)
3778          * @type Number
3779          */
3780         maxWidth : 600,
3781         /**
3782          * The minimum width in pixels of the message box (defaults to 100)
3783          * @type Number
3784          */
3785         minWidth : 100,
3786         /**
3787          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3788          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3789          * @type Number
3790          */
3791         minProgressWidth : 250,
3792         /**
3793          * An object containing the default button text strings that can be overriden for localized language support.
3794          * Supported properties are: ok, cancel, yes and no.
3795          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3796          * @type Object
3797          */
3798         buttonText : {
3799             ok : "OK",
3800             cancel : "Cancel",
3801             yes : "Yes",
3802             no : "No"
3803         }
3804     };
3805 }();
3806
3807 /**
3808  * Shorthand for {@link Roo.MessageBox}
3809  */
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3812 /*
3813  * - LGPL
3814  *
3815  * navbar
3816  * 
3817  */
3818
3819 /**
3820  * @class Roo.bootstrap.Navbar
3821  * @extends Roo.bootstrap.Component
3822  * Bootstrap Navbar class
3823
3824  * @constructor
3825  * Create a new Navbar
3826  * @param {Object} config The config object
3827  */
3828
3829
3830 Roo.bootstrap.Navbar = function(config){
3831     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3832     this.addEvents({
3833         // raw events
3834         /**
3835          * @event beforetoggle
3836          * Fire before toggle the menu
3837          * @param {Roo.EventObject} e
3838          */
3839         "beforetoggle" : true
3840     });
3841 };
3842
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3844     
3845     
3846    
3847     // private
3848     navItems : false,
3849     loadMask : false,
3850     
3851     
3852     getAutoCreate : function(){
3853         
3854         
3855         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3856         
3857     },
3858     
3859     initEvents :function ()
3860     {
3861         //Roo.log(this.el.select('.navbar-toggle',true));
3862         this.el.select('.navbar-toggle',true).on('click', function() {
3863             if(this.fireEvent('beforetoggle', this) !== false){
3864                 var ce = this.el.select('.navbar-collapse',true).first();
3865                 ce.toggleClass('in'); // old...
3866                 if (ce.hasClass('collapse')) {
3867                     // show it...
3868                     ce.removeClass('collapse');
3869                     ce.addClass('collapsing show');
3870                     (function() { ce.removeClass('collapsing'); }).defer(50);
3871                 } else {
3872                     ce.addClass('collapsing');
3873                     ce.removeClass('show');
3874                     (function() {
3875                         ce.removeClass('collapsing');
3876                         ce.addClass('collapse');
3877                     }).defer(1000);
3878                     
3879                 }
3880             }
3881             
3882         }, this);
3883         
3884         var mark = {
3885             tag: "div",
3886             cls:"x-dlg-mask"
3887         };
3888         
3889         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3890         
3891         var size = this.el.getSize();
3892         this.maskEl.setSize(size.width, size.height);
3893         this.maskEl.enableDisplayMode("block");
3894         this.maskEl.hide();
3895         
3896         if(this.loadMask){
3897             this.maskEl.show();
3898         }
3899     },
3900     
3901     
3902     getChildContainer : function()
3903     {
3904         if (this.el.select('.collapse').getCount()) {
3905             return this.el.select('.collapse',true).first();
3906         }
3907         
3908         return this.el;
3909     },
3910     
3911     mask : function()
3912     {
3913         this.maskEl.show();
3914     },
3915     
3916     unmask : function()
3917     {
3918         this.maskEl.hide();
3919     } 
3920     
3921     
3922     
3923     
3924 });
3925
3926
3927
3928  
3929
3930  /*
3931  * - LGPL
3932  *
3933  * navbar
3934  * 
3935  */
3936
3937 /**
3938  * @class Roo.bootstrap.NavSimplebar
3939  * @extends Roo.bootstrap.Navbar
3940  * Bootstrap Sidebar class
3941  *
3942  * @cfg {Boolean} inverse is inverted color
3943  * 
3944  * @cfg {String} type (nav | pills | tabs)
3945  * @cfg {Boolean} arrangement stacked | justified
3946  * @cfg {String} align (left | right) alignment
3947  * 
3948  * @cfg {Boolean} main (true|false) main nav bar? default false
3949  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3950  * 
3951  * @cfg {String} tag (header|footer|nav|div) default is nav 
3952
3953  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3954  * 
3955  * 
3956  * @constructor
3957  * Create a new Sidebar
3958  * @param {Object} config The config object
3959  */
3960
3961
3962 Roo.bootstrap.NavSimplebar = function(config){
3963     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3964 };
3965
3966 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3967     
3968     inverse: false,
3969     
3970     type: false,
3971     arrangement: '',
3972     align : false,
3973     
3974     weight : 'light',
3975     
3976     main : false,
3977     
3978     
3979     tag : false,
3980     
3981     
3982     getAutoCreate : function(){
3983         
3984         
3985         var cfg = {
3986             tag : this.tag || 'div',
3987             cls : 'navbar navbar-expand-lg'
3988         };
3989         if (['light','white'].indexOf(this.weight) > -1) {
3990             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3991         }
3992         cfg.cls += ' bg-' + this.weight;
3993         
3994           
3995         
3996         cfg.cn = [
3997             {
3998                 cls: 'nav',
3999                 tag : 'ul'
4000             }
4001         ];
4002         
4003          
4004         this.type = this.type || 'nav';
4005         if (['tabs','pills'].indexOf(this.type)!==-1) {
4006             cfg.cn[0].cls += ' nav-' + this.type
4007         
4008         
4009         } else {
4010             if (this.type!=='nav') {
4011                 Roo.log('nav type must be nav/tabs/pills')
4012             }
4013             cfg.cn[0].cls += ' navbar-nav'
4014         }
4015         
4016         
4017         
4018         
4019         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4020             cfg.cn[0].cls += ' nav-' + this.arrangement;
4021         }
4022         
4023         
4024         if (this.align === 'right') {
4025             cfg.cn[0].cls += ' navbar-right';
4026         }
4027         
4028         if (this.inverse) {
4029             cfg.cls += ' navbar-inverse';
4030             
4031         }
4032         
4033         
4034         return cfg;
4035     
4036         
4037     }
4038     
4039     
4040     
4041 });
4042
4043
4044
4045  
4046
4047  
4048        /*
4049  * - LGPL
4050  *
4051  * navbar
4052  * navbar-fixed-top
4053  * navbar-expand-md  fixed-top 
4054  */
4055
4056 /**
4057  * @class Roo.bootstrap.NavHeaderbar
4058  * @extends Roo.bootstrap.NavSimplebar
4059  * Bootstrap Sidebar class
4060  *
4061  * @cfg {String} brand what is brand
4062  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4063  * @cfg {String} brand_href href of the brand
4064  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4065  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4066  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4067  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4068  * 
4069  * @constructor
4070  * Create a new Sidebar
4071  * @param {Object} config The config object
4072  */
4073
4074
4075 Roo.bootstrap.NavHeaderbar = function(config){
4076     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4077       
4078 };
4079
4080 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4081     
4082     position: '',
4083     brand: '',
4084     brand_href: false,
4085     srButton : true,
4086     autohide : false,
4087     desktopCenter : false,
4088    
4089     
4090     getAutoCreate : function(){
4091         
4092         var   cfg = {
4093             tag: this.nav || 'nav',
4094             cls: 'navbar navbar-expand-md',
4095             role: 'navigation',
4096             cn: []
4097         };
4098         
4099         var cn = cfg.cn;
4100         if (this.desktopCenter) {
4101             cn.push({cls : 'container', cn : []});
4102             cn = cn[0].cn;
4103         }
4104         
4105         if(this.srButton){
4106             var btn = {
4107                 tag: 'button',
4108                 type: 'button',
4109                 cls: 'navbar-toggle navbar-toggler',
4110                 'data-toggle': 'collapse',
4111                 cn: [
4112                     {
4113                         tag: 'span',
4114                         cls: 'sr-only',
4115                         html: 'Toggle navigation'
4116                     },
4117                     {
4118                         tag: 'span',
4119                         cls: 'icon-bar navbar-toggler-icon'
4120                     },
4121                     {
4122                         tag: 'span',
4123                         cls: 'icon-bar'
4124                     },
4125                     {
4126                         tag: 'span',
4127                         cls: 'icon-bar'
4128                     }
4129                 ]
4130             };
4131             
4132             cn.push( Roo.bootstrap.version == 4 ? btn : {
4133                 tag: 'div',
4134                 cls: 'navbar-header',
4135                 cn: [
4136                     btn
4137                 ]
4138             });
4139         }
4140         
4141         cn.push({
4142             tag: 'div',
4143             cls: 'collapse navbar-collapse',
4144             cn : []
4145         });
4146         
4147         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4148         
4149         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4150             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4151             
4152             // tag can override this..
4153             
4154             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4155         }
4156         
4157         if (this.brand !== '') {
4158             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4159             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4160                 tag: 'a',
4161                 href: this.brand_href ? this.brand_href : '#',
4162                 cls: 'navbar-brand',
4163                 cn: [
4164                 this.brand
4165                 ]
4166             });
4167         }
4168         
4169         if(this.main){
4170             cfg.cls += ' main-nav';
4171         }
4172         
4173         
4174         return cfg;
4175
4176         
4177     },
4178     getHeaderChildContainer : function()
4179     {
4180         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4181             return this.el.select('.navbar-header',true).first();
4182         }
4183         
4184         return this.getChildContainer();
4185     },
4186     
4187     
4188     initEvents : function()
4189     {
4190         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4191         
4192         if (this.autohide) {
4193             
4194             var prevScroll = 0;
4195             var ft = this.el;
4196             
4197             Roo.get(document).on('scroll',function(e) {
4198                 var ns = Roo.get(document).getScroll().top;
4199                 var os = prevScroll;
4200                 prevScroll = ns;
4201                 
4202                 if(ns > os){
4203                     ft.removeClass('slideDown');
4204                     ft.addClass('slideUp');
4205                     return;
4206                 }
4207                 ft.removeClass('slideUp');
4208                 ft.addClass('slideDown');
4209                  
4210               
4211           },this);
4212         }
4213     }    
4214     
4215 });
4216
4217
4218
4219  
4220
4221  /*
4222  * - LGPL
4223  *
4224  * navbar
4225  * 
4226  */
4227
4228 /**
4229  * @class Roo.bootstrap.NavSidebar
4230  * @extends Roo.bootstrap.Navbar
4231  * Bootstrap Sidebar class
4232  * 
4233  * @constructor
4234  * Create a new Sidebar
4235  * @param {Object} config The config object
4236  */
4237
4238
4239 Roo.bootstrap.NavSidebar = function(config){
4240     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4241 };
4242
4243 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4244     
4245     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4246     
4247     getAutoCreate : function(){
4248         
4249         
4250         return  {
4251             tag: 'div',
4252             cls: 'sidebar sidebar-nav'
4253         };
4254     
4255         
4256     }
4257     
4258     
4259     
4260 });
4261
4262
4263
4264  
4265
4266  /*
4267  * - LGPL
4268  *
4269  * nav group
4270  * 
4271  */
4272
4273 /**
4274  * @class Roo.bootstrap.NavGroup
4275  * @extends Roo.bootstrap.Component
4276  * Bootstrap NavGroup class
4277  * @cfg {String} align (left|right)
4278  * @cfg {Boolean} inverse
4279  * @cfg {String} type (nav|pills|tab) default nav
4280  * @cfg {String} navId - reference Id for navbar.
4281
4282  * 
4283  * @constructor
4284  * Create a new nav group
4285  * @param {Object} config The config object
4286  */
4287
4288 Roo.bootstrap.NavGroup = function(config){
4289     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4290     this.navItems = [];
4291    
4292     Roo.bootstrap.NavGroup.register(this);
4293      this.addEvents({
4294         /**
4295              * @event changed
4296              * Fires when the active item changes
4297              * @param {Roo.bootstrap.NavGroup} this
4298              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4299              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4300          */
4301         'changed': true
4302      });
4303     
4304 };
4305
4306 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4307     
4308     align: '',
4309     inverse: false,
4310     form: false,
4311     type: 'nav',
4312     navId : '',
4313     // private
4314     
4315     navItems : false, 
4316     
4317     getAutoCreate : function()
4318     {
4319         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4320         
4321         cfg = {
4322             tag : 'ul',
4323             cls: 'nav' 
4324         };
4325         
4326         if (['tabs','pills'].indexOf(this.type)!==-1) {
4327             cfg.cls += ' nav-' + this.type
4328         } else {
4329             if (this.type!=='nav') {
4330                 Roo.log('nav type must be nav/tabs/pills')
4331             }
4332             cfg.cls += ' navbar-nav'
4333         }
4334         
4335         if (this.parent() && this.parent().sidebar) {
4336             cfg = {
4337                 tag: 'ul',
4338                 cls: 'dashboard-menu sidebar-menu'
4339             };
4340             
4341             return cfg;
4342         }
4343         
4344         if (this.form === true) {
4345             cfg = {
4346                 tag: 'form',
4347                 cls: 'navbar-form'
4348             };
4349             
4350             if (this.align === 'right') {
4351                 cfg.cls += ' navbar-right ml-md-auto';
4352             } else {
4353                 cfg.cls += ' navbar-left';
4354             }
4355         }
4356         
4357         if (this.align === 'right') {
4358             cfg.cls += ' navbar-right ml-md-auto';
4359         } else {
4360             cfg.cls += ' mr-auto';
4361         }
4362         
4363         if (this.inverse) {
4364             cfg.cls += ' navbar-inverse';
4365             
4366         }
4367         
4368         
4369         return cfg;
4370     },
4371     /**
4372     * sets the active Navigation item
4373     * @param {Roo.bootstrap.NavItem} the new current navitem
4374     */
4375     setActiveItem : function(item)
4376     {
4377         var prev = false;
4378         Roo.each(this.navItems, function(v){
4379             if (v == item) {
4380                 return ;
4381             }
4382             if (v.isActive()) {
4383                 v.setActive(false, true);
4384                 prev = v;
4385                 
4386             }
4387             
4388         });
4389
4390         item.setActive(true, true);
4391         this.fireEvent('changed', this, item, prev);
4392         
4393         
4394     },
4395     /**
4396     * gets the active Navigation item
4397     * @return {Roo.bootstrap.NavItem} the current navitem
4398     */
4399     getActive : function()
4400     {
4401         
4402         var prev = false;
4403         Roo.each(this.navItems, function(v){
4404             
4405             if (v.isActive()) {
4406                 prev = v;
4407                 
4408             }
4409             
4410         });
4411         return prev;
4412     },
4413     
4414     indexOfNav : function()
4415     {
4416         
4417         var prev = false;
4418         Roo.each(this.navItems, function(v,i){
4419             
4420             if (v.isActive()) {
4421                 prev = i;
4422                 
4423             }
4424             
4425         });
4426         return prev;
4427     },
4428     /**
4429     * adds a Navigation item
4430     * @param {Roo.bootstrap.NavItem} the navitem to add
4431     */
4432     addItem : function(cfg)
4433     {
4434         var cn = new Roo.bootstrap.NavItem(cfg);
4435         this.register(cn);
4436         cn.parentId = this.id;
4437         cn.onRender(this.el, null);
4438         return cn;
4439     },
4440     /**
4441     * register a Navigation item
4442     * @param {Roo.bootstrap.NavItem} the navitem to add
4443     */
4444     register : function(item)
4445     {
4446         this.navItems.push( item);
4447         item.navId = this.navId;
4448     
4449     },
4450     
4451     /**
4452     * clear all the Navigation item
4453     */
4454    
4455     clearAll : function()
4456     {
4457         this.navItems = [];
4458         this.el.dom.innerHTML = '';
4459     },
4460     
4461     getNavItem: function(tabId)
4462     {
4463         var ret = false;
4464         Roo.each(this.navItems, function(e) {
4465             if (e.tabId == tabId) {
4466                ret =  e;
4467                return false;
4468             }
4469             return true;
4470             
4471         });
4472         return ret;
4473     },
4474     
4475     setActiveNext : function()
4476     {
4477         var i = this.indexOfNav(this.getActive());
4478         if (i > this.navItems.length) {
4479             return;
4480         }
4481         this.setActiveItem(this.navItems[i+1]);
4482     },
4483     setActivePrev : function()
4484     {
4485         var i = this.indexOfNav(this.getActive());
4486         if (i  < 1) {
4487             return;
4488         }
4489         this.setActiveItem(this.navItems[i-1]);
4490     },
4491     clearWasActive : function(except) {
4492         Roo.each(this.navItems, function(e) {
4493             if (e.tabId != except.tabId && e.was_active) {
4494                e.was_active = false;
4495                return false;
4496             }
4497             return true;
4498             
4499         });
4500     },
4501     getWasActive : function ()
4502     {
4503         var r = false;
4504         Roo.each(this.navItems, function(e) {
4505             if (e.was_active) {
4506                r = e;
4507                return false;
4508             }
4509             return true;
4510             
4511         });
4512         return r;
4513     }
4514     
4515     
4516 });
4517
4518  
4519 Roo.apply(Roo.bootstrap.NavGroup, {
4520     
4521     groups: {},
4522      /**
4523     * register a Navigation Group
4524     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4525     */
4526     register : function(navgrp)
4527     {
4528         this.groups[navgrp.navId] = navgrp;
4529         
4530     },
4531     /**
4532     * fetch a Navigation Group based on the navigation ID
4533     * @param {string} the navgroup to add
4534     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4535     */
4536     get: function(navId) {
4537         if (typeof(this.groups[navId]) == 'undefined') {
4538             return false;
4539             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4540         }
4541         return this.groups[navId] ;
4542     }
4543     
4544     
4545     
4546 });
4547
4548  /*
4549  * - LGPL
4550  *
4551  * row
4552  * 
4553  */
4554
4555 /**
4556  * @class Roo.bootstrap.NavItem
4557  * @extends Roo.bootstrap.Component
4558  * Bootstrap Navbar.NavItem class
4559  * @cfg {String} href  link to
4560  * @cfg {String} html content of button
4561  * @cfg {String} badge text inside badge
4562  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4563  * @cfg {String} glyphicon DEPRICATED - use fa
4564  * @cfg {String} icon DEPRICATED - use fa
4565  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4566  * @cfg {Boolean} active Is item active
4567  * @cfg {Boolean} disabled Is item disabled
4568  
4569  * @cfg {Boolean} preventDefault (true | false) default false
4570  * @cfg {String} tabId the tab that this item activates.
4571  * @cfg {String} tagtype (a|span) render as a href or span?
4572  * @cfg {Boolean} animateRef (true|false) link to element default false  
4573   
4574  * @constructor
4575  * Create a new Navbar Item
4576  * @param {Object} config The config object
4577  */
4578 Roo.bootstrap.NavItem = function(config){
4579     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4580     this.addEvents({
4581         // raw events
4582         /**
4583          * @event click
4584          * The raw click event for the entire grid.
4585          * @param {Roo.EventObject} e
4586          */
4587         "click" : true,
4588          /**
4589             * @event changed
4590             * Fires when the active item active state changes
4591             * @param {Roo.bootstrap.NavItem} this
4592             * @param {boolean} state the new state
4593              
4594          */
4595         'changed': true,
4596         /**
4597             * @event scrollto
4598             * Fires when scroll to element
4599             * @param {Roo.bootstrap.NavItem} this
4600             * @param {Object} options
4601             * @param {Roo.EventObject} e
4602              
4603          */
4604         'scrollto': true
4605     });
4606    
4607 };
4608
4609 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4610     
4611     href: false,
4612     html: '',
4613     badge: '',
4614     icon: false,
4615     fa : false,
4616     glyphicon: false,
4617     active: false,
4618     preventDefault : false,
4619     tabId : false,
4620     tagtype : 'a',
4621     disabled : false,
4622     animateRef : false,
4623     was_active : false,
4624     
4625     getAutoCreate : function(){
4626          
4627         var cfg = {
4628             tag: 'li',
4629             cls: 'nav-item'
4630             
4631         };
4632         
4633         if (this.active) {
4634             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4635         }
4636         if (this.disabled) {
4637             cfg.cls += ' disabled';
4638         }
4639         
4640         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4641             cfg.cn = [
4642                 {
4643                     tag: this.tagtype,
4644                     href : this.href || "#",
4645                     html: this.html || ''
4646                 }
4647             ];
4648             if (this.tagtype == 'a') {
4649                 cfg.cn[0].cls = 'nav-link';
4650             }
4651             if (this.icon) {
4652                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4653             }
4654             if (this.fa) {
4655                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4656             }
4657             if(this.glyphicon) {
4658                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4659             }
4660             
4661             if (this.menu) {
4662                 
4663                 cfg.cn[0].html += " <span class='caret'></span>";
4664              
4665             }
4666             
4667             if (this.badge !== '') {
4668                  
4669                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4670             }
4671         }
4672         
4673         
4674         
4675         return cfg;
4676     },
4677     initEvents: function() 
4678     {
4679         if (typeof (this.menu) != 'undefined') {
4680             this.menu.parentType = this.xtype;
4681             this.menu.triggerEl = this.el;
4682             this.menu = this.addxtype(Roo.apply({}, this.menu));
4683         }
4684         
4685         this.el.select('a',true).on('click', this.onClick, this);
4686         
4687         if(this.tagtype == 'span'){
4688             this.el.select('span',true).on('click', this.onClick, this);
4689         }
4690        
4691         // at this point parent should be available..
4692         this.parent().register(this);
4693     },
4694     
4695     onClick : function(e)
4696     {
4697         if (e.getTarget('.dropdown-menu-item')) {
4698             // did you click on a menu itemm.... - then don't trigger onclick..
4699             return;
4700         }
4701         
4702         if(
4703                 this.preventDefault || 
4704                 this.href == '#' 
4705         ){
4706             Roo.log("NavItem - prevent Default?");
4707             e.preventDefault();
4708         }
4709         
4710         if (this.disabled) {
4711             return;
4712         }
4713         
4714         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4715         if (tg && tg.transition) {
4716             Roo.log("waiting for the transitionend");
4717             return;
4718         }
4719         
4720         
4721         
4722         //Roo.log("fire event clicked");
4723         if(this.fireEvent('click', this, e) === false){
4724             return;
4725         };
4726         
4727         if(this.tagtype == 'span'){
4728             return;
4729         }
4730         
4731         //Roo.log(this.href);
4732         var ael = this.el.select('a',true).first();
4733         //Roo.log(ael);
4734         
4735         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4736             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4737             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4738                 return; // ignore... - it's a 'hash' to another page.
4739             }
4740             Roo.log("NavItem - prevent Default?");
4741             e.preventDefault();
4742             this.scrollToElement(e);
4743         }
4744         
4745         
4746         var p =  this.parent();
4747    
4748         if (['tabs','pills'].indexOf(p.type)!==-1) {
4749             if (typeof(p.setActiveItem) !== 'undefined') {
4750                 p.setActiveItem(this);
4751             }
4752         }
4753         
4754         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4755         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4756             // remove the collapsed menu expand...
4757             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4758         }
4759     },
4760     
4761     isActive: function () {
4762         return this.active
4763     },
4764     setActive : function(state, fire, is_was_active)
4765     {
4766         if (this.active && !state && this.navId) {
4767             this.was_active = true;
4768             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4769             if (nv) {
4770                 nv.clearWasActive(this);
4771             }
4772             
4773         }
4774         this.active = state;
4775         
4776         if (!state ) {
4777             this.el.removeClass('active');
4778         } else if (!this.el.hasClass('active')) {
4779             this.el.addClass('active');
4780         }
4781         if (fire) {
4782             this.fireEvent('changed', this, state);
4783         }
4784         
4785         // show a panel if it's registered and related..
4786         
4787         if (!this.navId || !this.tabId || !state || is_was_active) {
4788             return;
4789         }
4790         
4791         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4792         if (!tg) {
4793             return;
4794         }
4795         var pan = tg.getPanelByName(this.tabId);
4796         if (!pan) {
4797             return;
4798         }
4799         // if we can not flip to new panel - go back to old nav highlight..
4800         if (false == tg.showPanel(pan)) {
4801             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4802             if (nv) {
4803                 var onav = nv.getWasActive();
4804                 if (onav) {
4805                     onav.setActive(true, false, true);
4806                 }
4807             }
4808             
4809         }
4810         
4811         
4812         
4813     },
4814      // this should not be here...
4815     setDisabled : function(state)
4816     {
4817         this.disabled = state;
4818         if (!state ) {
4819             this.el.removeClass('disabled');
4820         } else if (!this.el.hasClass('disabled')) {
4821             this.el.addClass('disabled');
4822         }
4823         
4824     },
4825     
4826     /**
4827      * Fetch the element to display the tooltip on.
4828      * @return {Roo.Element} defaults to this.el
4829      */
4830     tooltipEl : function()
4831     {
4832         return this.el.select('' + this.tagtype + '', true).first();
4833     },
4834     
4835     scrollToElement : function(e)
4836     {
4837         var c = document.body;
4838         
4839         /*
4840          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4841          */
4842         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4843             c = document.documentElement;
4844         }
4845         
4846         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4847         
4848         if(!target){
4849             return;
4850         }
4851
4852         var o = target.calcOffsetsTo(c);
4853         
4854         var options = {
4855             target : target,
4856             value : o[1]
4857         };
4858         
4859         this.fireEvent('scrollto', this, options, e);
4860         
4861         Roo.get(c).scrollTo('top', options.value, true);
4862         
4863         return;
4864     }
4865 });
4866  
4867
4868  /*
4869  * - LGPL
4870  *
4871  * sidebar item
4872  *
4873  *  li
4874  *    <span> icon </span>
4875  *    <span> text </span>
4876  *    <span>badge </span>
4877  */
4878
4879 /**
4880  * @class Roo.bootstrap.NavSidebarItem
4881  * @extends Roo.bootstrap.NavItem
4882  * Bootstrap Navbar.NavSidebarItem class
4883  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4884  * {Boolean} open is the menu open
4885  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4886  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4887  * {String} buttonSize (sm|md|lg)the extra classes for the button
4888  * {Boolean} showArrow show arrow next to the text (default true)
4889  * @constructor
4890  * Create a new Navbar Button
4891  * @param {Object} config The config object
4892  */
4893 Roo.bootstrap.NavSidebarItem = function(config){
4894     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4895     this.addEvents({
4896         // raw events
4897         /**
4898          * @event click
4899          * The raw click event for the entire grid.
4900          * @param {Roo.EventObject} e
4901          */
4902         "click" : true,
4903          /**
4904             * @event changed
4905             * Fires when the active item active state changes
4906             * @param {Roo.bootstrap.NavSidebarItem} this
4907             * @param {boolean} state the new state
4908              
4909          */
4910         'changed': true
4911     });
4912    
4913 };
4914
4915 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4916     
4917     badgeWeight : 'default',
4918     
4919     open: false,
4920     
4921     buttonView : false,
4922     
4923     buttonWeight : 'default',
4924     
4925     buttonSize : 'md',
4926     
4927     showArrow : true,
4928     
4929     getAutoCreate : function(){
4930         
4931         
4932         var a = {
4933                 tag: 'a',
4934                 href : this.href || '#',
4935                 cls: '',
4936                 html : '',
4937                 cn : []
4938         };
4939         
4940         if(this.buttonView){
4941             a = {
4942                 tag: 'button',
4943                 href : this.href || '#',
4944                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4945                 html : this.html,
4946                 cn : []
4947             };
4948         }
4949         
4950         var cfg = {
4951             tag: 'li',
4952             cls: '',
4953             cn: [ a ]
4954         };
4955         
4956         if (this.active) {
4957             cfg.cls += ' active';
4958         }
4959         
4960         if (this.disabled) {
4961             cfg.cls += ' disabled';
4962         }
4963         if (this.open) {
4964             cfg.cls += ' open x-open';
4965         }
4966         // left icon..
4967         if (this.glyphicon || this.icon) {
4968             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4969             a.cn.push({ tag : 'i', cls : c }) ;
4970         }
4971         
4972         if(!this.buttonView){
4973             var span = {
4974                 tag: 'span',
4975                 html : this.html || ''
4976             };
4977
4978             a.cn.push(span);
4979             
4980         }
4981         
4982         if (this.badge !== '') {
4983             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4984         }
4985         
4986         if (this.menu) {
4987             
4988             if(this.showArrow){
4989                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4990             }
4991             
4992             a.cls += ' dropdown-toggle treeview' ;
4993         }
4994         
4995         return cfg;
4996     },
4997     
4998     initEvents : function()
4999     { 
5000         if (typeof (this.menu) != 'undefined') {
5001             this.menu.parentType = this.xtype;
5002             this.menu.triggerEl = this.el;
5003             this.menu = this.addxtype(Roo.apply({}, this.menu));
5004         }
5005         
5006         this.el.on('click', this.onClick, this);
5007         
5008         if(this.badge !== ''){
5009             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5010         }
5011         
5012     },
5013     
5014     onClick : function(e)
5015     {
5016         if(this.disabled){
5017             e.preventDefault();
5018             return;
5019         }
5020         
5021         if(this.preventDefault){
5022             e.preventDefault();
5023         }
5024         
5025         this.fireEvent('click', this);
5026     },
5027     
5028     disable : function()
5029     {
5030         this.setDisabled(true);
5031     },
5032     
5033     enable : function()
5034     {
5035         this.setDisabled(false);
5036     },
5037     
5038     setDisabled : function(state)
5039     {
5040         if(this.disabled == state){
5041             return;
5042         }
5043         
5044         this.disabled = state;
5045         
5046         if (state) {
5047             this.el.addClass('disabled');
5048             return;
5049         }
5050         
5051         this.el.removeClass('disabled');
5052         
5053         return;
5054     },
5055     
5056     setActive : function(state)
5057     {
5058         if(this.active == state){
5059             return;
5060         }
5061         
5062         this.active = state;
5063         
5064         if (state) {
5065             this.el.addClass('active');
5066             return;
5067         }
5068         
5069         this.el.removeClass('active');
5070         
5071         return;
5072     },
5073     
5074     isActive: function () 
5075     {
5076         return this.active;
5077     },
5078     
5079     setBadge : function(str)
5080     {
5081         if(!this.badgeEl){
5082             return;
5083         }
5084         
5085         this.badgeEl.dom.innerHTML = str;
5086     }
5087     
5088    
5089      
5090  
5091 });
5092  
5093
5094  /*
5095  * - LGPL
5096  *
5097  * row
5098  * 
5099  */
5100
5101 /**
5102  * @class Roo.bootstrap.Row
5103  * @extends Roo.bootstrap.Component
5104  * Bootstrap Row class (contains columns...)
5105  * 
5106  * @constructor
5107  * Create a new Row
5108  * @param {Object} config The config object
5109  */
5110
5111 Roo.bootstrap.Row = function(config){
5112     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5113 };
5114
5115 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5116     
5117     getAutoCreate : function(){
5118        return {
5119             cls: 'row clearfix'
5120        };
5121     }
5122     
5123     
5124 });
5125
5126  
5127
5128  /*
5129  * - LGPL
5130  *
5131  * element
5132  * 
5133  */
5134
5135 /**
5136  * @class Roo.bootstrap.Element
5137  * @extends Roo.bootstrap.Component
5138  * Bootstrap Element class
5139  * @cfg {String} html contents of the element
5140  * @cfg {String} tag tag of the element
5141  * @cfg {String} cls class of the element
5142  * @cfg {Boolean} preventDefault (true|false) default false
5143  * @cfg {Boolean} clickable (true|false) default false
5144  * 
5145  * @constructor
5146  * Create a new Element
5147  * @param {Object} config The config object
5148  */
5149
5150 Roo.bootstrap.Element = function(config){
5151     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5152     
5153     this.addEvents({
5154         // raw events
5155         /**
5156          * @event click
5157          * When a element is chick
5158          * @param {Roo.bootstrap.Element} this
5159          * @param {Roo.EventObject} e
5160          */
5161         "click" : true
5162     });
5163 };
5164
5165 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5166     
5167     tag: 'div',
5168     cls: '',
5169     html: '',
5170     preventDefault: false, 
5171     clickable: false,
5172     
5173     getAutoCreate : function(){
5174         
5175         var cfg = {
5176             tag: this.tag,
5177             // cls: this.cls, double assign in parent class Component.js :: onRender
5178             html: this.html
5179         };
5180         
5181         return cfg;
5182     },
5183     
5184     initEvents: function() 
5185     {
5186         Roo.bootstrap.Element.superclass.initEvents.call(this);
5187         
5188         if(this.clickable){
5189             this.el.on('click', this.onClick, this);
5190         }
5191         
5192     },
5193     
5194     onClick : function(e)
5195     {
5196         if(this.preventDefault){
5197             e.preventDefault();
5198         }
5199         
5200         this.fireEvent('click', this, e);
5201     },
5202     
5203     getValue : function()
5204     {
5205         return this.el.dom.innerHTML;
5206     },
5207     
5208     setValue : function(value)
5209     {
5210         this.el.dom.innerHTML = value;
5211     }
5212    
5213 });
5214
5215  
5216
5217  /*
5218  * - LGPL
5219  *
5220  * pagination
5221  * 
5222  */
5223
5224 /**
5225  * @class Roo.bootstrap.Pagination
5226  * @extends Roo.bootstrap.Component
5227  * Bootstrap Pagination class
5228  * @cfg {String} size xs | sm | md | lg
5229  * @cfg {Boolean} inverse false | true
5230  * 
5231  * @constructor
5232  * Create a new Pagination
5233  * @param {Object} config The config object
5234  */
5235
5236 Roo.bootstrap.Pagination = function(config){
5237     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5238 };
5239
5240 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5241     
5242     cls: false,
5243     size: false,
5244     inverse: false,
5245     
5246     getAutoCreate : function(){
5247         var cfg = {
5248             tag: 'ul',
5249                 cls: 'pagination'
5250         };
5251         if (this.inverse) {
5252             cfg.cls += ' inverse';
5253         }
5254         if (this.html) {
5255             cfg.html=this.html;
5256         }
5257         if (this.cls) {
5258             cfg.cls += " " + this.cls;
5259         }
5260         return cfg;
5261     }
5262    
5263 });
5264
5265  
5266
5267  /*
5268  * - LGPL
5269  *
5270  * Pagination item
5271  * 
5272  */
5273
5274
5275 /**
5276  * @class Roo.bootstrap.PaginationItem
5277  * @extends Roo.bootstrap.Component
5278  * Bootstrap PaginationItem class
5279  * @cfg {String} html text
5280  * @cfg {String} href the link
5281  * @cfg {Boolean} preventDefault (true | false) default true
5282  * @cfg {Boolean} active (true | false) default false
5283  * @cfg {Boolean} disabled default false
5284  * 
5285  * 
5286  * @constructor
5287  * Create a new PaginationItem
5288  * @param {Object} config The config object
5289  */
5290
5291
5292 Roo.bootstrap.PaginationItem = function(config){
5293     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5294     this.addEvents({
5295         // raw events
5296         /**
5297          * @event click
5298          * The raw click event for the entire grid.
5299          * @param {Roo.EventObject} e
5300          */
5301         "click" : true
5302     });
5303 };
5304
5305 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5306     
5307     href : false,
5308     html : false,
5309     preventDefault: true,
5310     active : false,
5311     cls : false,
5312     disabled: false,
5313     
5314     getAutoCreate : function(){
5315         var cfg= {
5316             tag: 'li',
5317             cn: [
5318                 {
5319                     tag : 'a',
5320                     href : this.href ? this.href : '#',
5321                     html : this.html ? this.html : ''
5322                 }
5323             ]
5324         };
5325         
5326         if(this.cls){
5327             cfg.cls = this.cls;
5328         }
5329         
5330         if(this.disabled){
5331             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5332         }
5333         
5334         if(this.active){
5335             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5336         }
5337         
5338         return cfg;
5339     },
5340     
5341     initEvents: function() {
5342         
5343         this.el.on('click', this.onClick, this);
5344         
5345     },
5346     onClick : function(e)
5347     {
5348         Roo.log('PaginationItem on click ');
5349         if(this.preventDefault){
5350             e.preventDefault();
5351         }
5352         
5353         if(this.disabled){
5354             return;
5355         }
5356         
5357         this.fireEvent('click', this, e);
5358     }
5359    
5360 });
5361
5362  
5363
5364  /*
5365  * - LGPL
5366  *
5367  * slider
5368  * 
5369  */
5370
5371
5372 /**
5373  * @class Roo.bootstrap.Slider
5374  * @extends Roo.bootstrap.Component
5375  * Bootstrap Slider class
5376  *    
5377  * @constructor
5378  * Create a new Slider
5379  * @param {Object} config The config object
5380  */
5381
5382 Roo.bootstrap.Slider = function(config){
5383     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5384 };
5385
5386 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5387     
5388     getAutoCreate : function(){
5389         
5390         var cfg = {
5391             tag: 'div',
5392             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5393             cn: [
5394                 {
5395                     tag: 'a',
5396                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5397                 }
5398             ]
5399         };
5400         
5401         return cfg;
5402     }
5403    
5404 });
5405
5406  /*
5407  * Based on:
5408  * Ext JS Library 1.1.1
5409  * Copyright(c) 2006-2007, Ext JS, LLC.
5410  *
5411  * Originally Released Under LGPL - original licence link has changed is not relivant.
5412  *
5413  * Fork - LGPL
5414  * <script type="text/javascript">
5415  */
5416  
5417
5418 /**
5419  * @class Roo.grid.ColumnModel
5420  * @extends Roo.util.Observable
5421  * This is the default implementation of a ColumnModel used by the Grid. It defines
5422  * the columns in the grid.
5423  * <br>Usage:<br>
5424  <pre><code>
5425  var colModel = new Roo.grid.ColumnModel([
5426         {header: "Ticker", width: 60, sortable: true, locked: true},
5427         {header: "Company Name", width: 150, sortable: true},
5428         {header: "Market Cap.", width: 100, sortable: true},
5429         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5430         {header: "Employees", width: 100, sortable: true, resizable: false}
5431  ]);
5432  </code></pre>
5433  * <p>
5434  
5435  * The config options listed for this class are options which may appear in each
5436  * individual column definition.
5437  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5438  * @constructor
5439  * @param {Object} config An Array of column config objects. See this class's
5440  * config objects for details.
5441 */
5442 Roo.grid.ColumnModel = function(config){
5443         /**
5444      * The config passed into the constructor
5445      */
5446     this.config = config;
5447     this.lookup = {};
5448
5449     // if no id, create one
5450     // if the column does not have a dataIndex mapping,
5451     // map it to the order it is in the config
5452     for(var i = 0, len = config.length; i < len; i++){
5453         var c = config[i];
5454         if(typeof c.dataIndex == "undefined"){
5455             c.dataIndex = i;
5456         }
5457         if(typeof c.renderer == "string"){
5458             c.renderer = Roo.util.Format[c.renderer];
5459         }
5460         if(typeof c.id == "undefined"){
5461             c.id = Roo.id();
5462         }
5463         if(c.editor && c.editor.xtype){
5464             c.editor  = Roo.factory(c.editor, Roo.grid);
5465         }
5466         if(c.editor && c.editor.isFormField){
5467             c.editor = new Roo.grid.GridEditor(c.editor);
5468         }
5469         this.lookup[c.id] = c;
5470     }
5471
5472     /**
5473      * The width of columns which have no width specified (defaults to 100)
5474      * @type Number
5475      */
5476     this.defaultWidth = 100;
5477
5478     /**
5479      * Default sortable of columns which have no sortable specified (defaults to false)
5480      * @type Boolean
5481      */
5482     this.defaultSortable = false;
5483
5484     this.addEvents({
5485         /**
5486              * @event widthchange
5487              * Fires when the width of a column changes.
5488              * @param {ColumnModel} this
5489              * @param {Number} columnIndex The column index
5490              * @param {Number} newWidth The new width
5491              */
5492             "widthchange": true,
5493         /**
5494              * @event headerchange
5495              * Fires when the text of a header changes.
5496              * @param {ColumnModel} this
5497              * @param {Number} columnIndex The column index
5498              * @param {Number} newText The new header text
5499              */
5500             "headerchange": true,
5501         /**
5502              * @event hiddenchange
5503              * Fires when a column is hidden or "unhidden".
5504              * @param {ColumnModel} this
5505              * @param {Number} columnIndex The column index
5506              * @param {Boolean} hidden true if hidden, false otherwise
5507              */
5508             "hiddenchange": true,
5509             /**
5510          * @event columnmoved
5511          * Fires when a column is moved.
5512          * @param {ColumnModel} this
5513          * @param {Number} oldIndex
5514          * @param {Number} newIndex
5515          */
5516         "columnmoved" : true,
5517         /**
5518          * @event columlockchange
5519          * Fires when a column's locked state is changed
5520          * @param {ColumnModel} this
5521          * @param {Number} colIndex
5522          * @param {Boolean} locked true if locked
5523          */
5524         "columnlockchange" : true
5525     });
5526     Roo.grid.ColumnModel.superclass.constructor.call(this);
5527 };
5528 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5529     /**
5530      * @cfg {String} header The header text to display in the Grid view.
5531      */
5532     /**
5533      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5534      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5535      * specified, the column's index is used as an index into the Record's data Array.
5536      */
5537     /**
5538      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5539      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5540      */
5541     /**
5542      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5543      * Defaults to the value of the {@link #defaultSortable} property.
5544      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5545      */
5546     /**
5547      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5548      */
5549     /**
5550      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5551      */
5552     /**
5553      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5554      */
5555     /**
5556      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5557      */
5558     /**
5559      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5560      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5561      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5562      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5563      */
5564        /**
5565      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5566      */
5567     /**
5568      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5569      */
5570     /**
5571      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5572      */
5573     /**
5574      * @cfg {String} cursor (Optional)
5575      */
5576     /**
5577      * @cfg {String} tooltip (Optional)
5578      */
5579     /**
5580      * @cfg {Number} xs (Optional)
5581      */
5582     /**
5583      * @cfg {Number} sm (Optional)
5584      */
5585     /**
5586      * @cfg {Number} md (Optional)
5587      */
5588     /**
5589      * @cfg {Number} lg (Optional)
5590      */
5591     /**
5592      * Returns the id of the column at the specified index.
5593      * @param {Number} index The column index
5594      * @return {String} the id
5595      */
5596     getColumnId : function(index){
5597         return this.config[index].id;
5598     },
5599
5600     /**
5601      * Returns the column for a specified id.
5602      * @param {String} id The column id
5603      * @return {Object} the column
5604      */
5605     getColumnById : function(id){
5606         return this.lookup[id];
5607     },
5608
5609     
5610     /**
5611      * Returns the column for a specified dataIndex.
5612      * @param {String} dataIndex The column dataIndex
5613      * @return {Object|Boolean} the column or false if not found
5614      */
5615     getColumnByDataIndex: function(dataIndex){
5616         var index = this.findColumnIndex(dataIndex);
5617         return index > -1 ? this.config[index] : false;
5618     },
5619     
5620     /**
5621      * Returns the index for a specified column id.
5622      * @param {String} id The column id
5623      * @return {Number} the index, or -1 if not found
5624      */
5625     getIndexById : function(id){
5626         for(var i = 0, len = this.config.length; i < len; i++){
5627             if(this.config[i].id == id){
5628                 return i;
5629             }
5630         }
5631         return -1;
5632     },
5633     
5634     /**
5635      * Returns the index for a specified column dataIndex.
5636      * @param {String} dataIndex The column dataIndex
5637      * @return {Number} the index, or -1 if not found
5638      */
5639     
5640     findColumnIndex : function(dataIndex){
5641         for(var i = 0, len = this.config.length; i < len; i++){
5642             if(this.config[i].dataIndex == dataIndex){
5643                 return i;
5644             }
5645         }
5646         return -1;
5647     },
5648     
5649     
5650     moveColumn : function(oldIndex, newIndex){
5651         var c = this.config[oldIndex];
5652         this.config.splice(oldIndex, 1);
5653         this.config.splice(newIndex, 0, c);
5654         this.dataMap = null;
5655         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5656     },
5657
5658     isLocked : function(colIndex){
5659         return this.config[colIndex].locked === true;
5660     },
5661
5662     setLocked : function(colIndex, value, suppressEvent){
5663         if(this.isLocked(colIndex) == value){
5664             return;
5665         }
5666         this.config[colIndex].locked = value;
5667         if(!suppressEvent){
5668             this.fireEvent("columnlockchange", this, colIndex, value);
5669         }
5670     },
5671
5672     getTotalLockedWidth : function(){
5673         var totalWidth = 0;
5674         for(var i = 0; i < this.config.length; i++){
5675             if(this.isLocked(i) && !this.isHidden(i)){
5676                 this.totalWidth += this.getColumnWidth(i);
5677             }
5678         }
5679         return totalWidth;
5680     },
5681
5682     getLockedCount : function(){
5683         for(var i = 0, len = this.config.length; i < len; i++){
5684             if(!this.isLocked(i)){
5685                 return i;
5686             }
5687         }
5688         
5689         return this.config.length;
5690     },
5691
5692     /**
5693      * Returns the number of columns.
5694      * @return {Number}
5695      */
5696     getColumnCount : function(visibleOnly){
5697         if(visibleOnly === true){
5698             var c = 0;
5699             for(var i = 0, len = this.config.length; i < len; i++){
5700                 if(!this.isHidden(i)){
5701                     c++;
5702                 }
5703             }
5704             return c;
5705         }
5706         return this.config.length;
5707     },
5708
5709     /**
5710      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5711      * @param {Function} fn
5712      * @param {Object} scope (optional)
5713      * @return {Array} result
5714      */
5715     getColumnsBy : function(fn, scope){
5716         var r = [];
5717         for(var i = 0, len = this.config.length; i < len; i++){
5718             var c = this.config[i];
5719             if(fn.call(scope||this, c, i) === true){
5720                 r[r.length] = c;
5721             }
5722         }
5723         return r;
5724     },
5725
5726     /**
5727      * Returns true if the specified column is sortable.
5728      * @param {Number} col The column index
5729      * @return {Boolean}
5730      */
5731     isSortable : function(col){
5732         if(typeof this.config[col].sortable == "undefined"){
5733             return this.defaultSortable;
5734         }
5735         return this.config[col].sortable;
5736     },
5737
5738     /**
5739      * Returns the rendering (formatting) function defined for the column.
5740      * @param {Number} col The column index.
5741      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5742      */
5743     getRenderer : function(col){
5744         if(!this.config[col].renderer){
5745             return Roo.grid.ColumnModel.defaultRenderer;
5746         }
5747         return this.config[col].renderer;
5748     },
5749
5750     /**
5751      * Sets the rendering (formatting) function for a column.
5752      * @param {Number} col The column index
5753      * @param {Function} fn The function to use to process the cell's raw data
5754      * to return HTML markup for the grid view. The render function is called with
5755      * the following parameters:<ul>
5756      * <li>Data value.</li>
5757      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5758      * <li>css A CSS style string to apply to the table cell.</li>
5759      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5760      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5761      * <li>Row index</li>
5762      * <li>Column index</li>
5763      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5764      */
5765     setRenderer : function(col, fn){
5766         this.config[col].renderer = fn;
5767     },
5768
5769     /**
5770      * Returns the width for the specified column.
5771      * @param {Number} col The column index
5772      * @return {Number}
5773      */
5774     getColumnWidth : function(col){
5775         return this.config[col].width * 1 || this.defaultWidth;
5776     },
5777
5778     /**
5779      * Sets the width for a column.
5780      * @param {Number} col The column index
5781      * @param {Number} width The new width
5782      */
5783     setColumnWidth : function(col, width, suppressEvent){
5784         this.config[col].width = width;
5785         this.totalWidth = null;
5786         if(!suppressEvent){
5787              this.fireEvent("widthchange", this, col, width);
5788         }
5789     },
5790
5791     /**
5792      * Returns the total width of all columns.
5793      * @param {Boolean} includeHidden True to include hidden column widths
5794      * @return {Number}
5795      */
5796     getTotalWidth : function(includeHidden){
5797         if(!this.totalWidth){
5798             this.totalWidth = 0;
5799             for(var i = 0, len = this.config.length; i < len; i++){
5800                 if(includeHidden || !this.isHidden(i)){
5801                     this.totalWidth += this.getColumnWidth(i);
5802                 }
5803             }
5804         }
5805         return this.totalWidth;
5806     },
5807
5808     /**
5809      * Returns the header for the specified column.
5810      * @param {Number} col The column index
5811      * @return {String}
5812      */
5813     getColumnHeader : function(col){
5814         return this.config[col].header;
5815     },
5816
5817     /**
5818      * Sets the header for a column.
5819      * @param {Number} col The column index
5820      * @param {String} header The new header
5821      */
5822     setColumnHeader : function(col, header){
5823         this.config[col].header = header;
5824         this.fireEvent("headerchange", this, col, header);
5825     },
5826
5827     /**
5828      * Returns the tooltip for the specified column.
5829      * @param {Number} col The column index
5830      * @return {String}
5831      */
5832     getColumnTooltip : function(col){
5833             return this.config[col].tooltip;
5834     },
5835     /**
5836      * Sets the tooltip for a column.
5837      * @param {Number} col The column index
5838      * @param {String} tooltip The new tooltip
5839      */
5840     setColumnTooltip : function(col, tooltip){
5841             this.config[col].tooltip = tooltip;
5842     },
5843
5844     /**
5845      * Returns the dataIndex for the specified column.
5846      * @param {Number} col The column index
5847      * @return {Number}
5848      */
5849     getDataIndex : function(col){
5850         return this.config[col].dataIndex;
5851     },
5852
5853     /**
5854      * Sets the dataIndex for a column.
5855      * @param {Number} col The column index
5856      * @param {Number} dataIndex The new dataIndex
5857      */
5858     setDataIndex : function(col, dataIndex){
5859         this.config[col].dataIndex = dataIndex;
5860     },
5861
5862     
5863     
5864     /**
5865      * Returns true if the cell is editable.
5866      * @param {Number} colIndex The column index
5867      * @param {Number} rowIndex The row index - this is nto actually used..?
5868      * @return {Boolean}
5869      */
5870     isCellEditable : function(colIndex, rowIndex){
5871         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5872     },
5873
5874     /**
5875      * Returns the editor defined for the cell/column.
5876      * return false or null to disable editing.
5877      * @param {Number} colIndex The column index
5878      * @param {Number} rowIndex The row index
5879      * @return {Object}
5880      */
5881     getCellEditor : function(colIndex, rowIndex){
5882         return this.config[colIndex].editor;
5883     },
5884
5885     /**
5886      * Sets if a column is editable.
5887      * @param {Number} col The column index
5888      * @param {Boolean} editable True if the column is editable
5889      */
5890     setEditable : function(col, editable){
5891         this.config[col].editable = editable;
5892     },
5893
5894
5895     /**
5896      * Returns true if the column is hidden.
5897      * @param {Number} colIndex The column index
5898      * @return {Boolean}
5899      */
5900     isHidden : function(colIndex){
5901         return this.config[colIndex].hidden;
5902     },
5903
5904
5905     /**
5906      * Returns true if the column width cannot be changed
5907      */
5908     isFixed : function(colIndex){
5909         return this.config[colIndex].fixed;
5910     },
5911
5912     /**
5913      * Returns true if the column can be resized
5914      * @return {Boolean}
5915      */
5916     isResizable : function(colIndex){
5917         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5918     },
5919     /**
5920      * Sets if a column is hidden.
5921      * @param {Number} colIndex The column index
5922      * @param {Boolean} hidden True if the column is hidden
5923      */
5924     setHidden : function(colIndex, hidden){
5925         this.config[colIndex].hidden = hidden;
5926         this.totalWidth = null;
5927         this.fireEvent("hiddenchange", this, colIndex, hidden);
5928     },
5929
5930     /**
5931      * Sets the editor for a column.
5932      * @param {Number} col The column index
5933      * @param {Object} editor The editor object
5934      */
5935     setEditor : function(col, editor){
5936         this.config[col].editor = editor;
5937     }
5938 });
5939
5940 Roo.grid.ColumnModel.defaultRenderer = function(value)
5941 {
5942     if(typeof value == "object") {
5943         return value;
5944     }
5945         if(typeof value == "string" && value.length < 1){
5946             return "&#160;";
5947         }
5948     
5949         return String.format("{0}", value);
5950 };
5951
5952 // Alias for backwards compatibility
5953 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5954 /*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964  
5965 /**
5966  * @class Roo.LoadMask
5967  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5968  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5969  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5970  * element's UpdateManager load indicator and will be destroyed after the initial load.
5971  * @constructor
5972  * Create a new LoadMask
5973  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5974  * @param {Object} config The config object
5975  */
5976 Roo.LoadMask = function(el, config){
5977     this.el = Roo.get(el);
5978     Roo.apply(this, config);
5979     if(this.store){
5980         this.store.on('beforeload', this.onBeforeLoad, this);
5981         this.store.on('load', this.onLoad, this);
5982         this.store.on('loadexception', this.onLoadException, this);
5983         this.removeMask = false;
5984     }else{
5985         var um = this.el.getUpdateManager();
5986         um.showLoadIndicator = false; // disable the default indicator
5987         um.on('beforeupdate', this.onBeforeLoad, this);
5988         um.on('update', this.onLoad, this);
5989         um.on('failure', this.onLoad, this);
5990         this.removeMask = true;
5991     }
5992 };
5993
5994 Roo.LoadMask.prototype = {
5995     /**
5996      * @cfg {Boolean} removeMask
5997      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5998      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5999      */
6000     /**
6001      * @cfg {String} msg
6002      * The text to display in a centered loading message box (defaults to 'Loading...')
6003      */
6004     msg : 'Loading...',
6005     /**
6006      * @cfg {String} msgCls
6007      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6008      */
6009     msgCls : 'x-mask-loading',
6010
6011     /**
6012      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6013      * @type Boolean
6014      */
6015     disabled: false,
6016
6017     /**
6018      * Disables the mask to prevent it from being displayed
6019      */
6020     disable : function(){
6021        this.disabled = true;
6022     },
6023
6024     /**
6025      * Enables the mask so that it can be displayed
6026      */
6027     enable : function(){
6028         this.disabled = false;
6029     },
6030     
6031     onLoadException : function()
6032     {
6033         Roo.log(arguments);
6034         
6035         if (typeof(arguments[3]) != 'undefined') {
6036             Roo.MessageBox.alert("Error loading",arguments[3]);
6037         } 
6038         /*
6039         try {
6040             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6041                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6042             }   
6043         } catch(e) {
6044             
6045         }
6046         */
6047     
6048         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6049     },
6050     // private
6051     onLoad : function()
6052     {
6053         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6054     },
6055
6056     // private
6057     onBeforeLoad : function(){
6058         if(!this.disabled){
6059             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6060         }
6061     },
6062
6063     // private
6064     destroy : function(){
6065         if(this.store){
6066             this.store.un('beforeload', this.onBeforeLoad, this);
6067             this.store.un('load', this.onLoad, this);
6068             this.store.un('loadexception', this.onLoadException, this);
6069         }else{
6070             var um = this.el.getUpdateManager();
6071             um.un('beforeupdate', this.onBeforeLoad, this);
6072             um.un('update', this.onLoad, this);
6073             um.un('failure', this.onLoad, this);
6074         }
6075     }
6076 };/*
6077  * - LGPL
6078  *
6079  * table
6080  * 
6081  */
6082
6083 /**
6084  * @class Roo.bootstrap.Table
6085  * @extends Roo.bootstrap.Component
6086  * Bootstrap Table class
6087  * @cfg {String} cls table class
6088  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6089  * @cfg {String} bgcolor Specifies the background color for a table
6090  * @cfg {Number} border Specifies whether the table cells should have borders or not
6091  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6092  * @cfg {Number} cellspacing Specifies the space between cells
6093  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6094  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6095  * @cfg {String} sortable Specifies that the table should be sortable
6096  * @cfg {String} summary Specifies a summary of the content of a table
6097  * @cfg {Number} width Specifies the width of a table
6098  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6099  * 
6100  * @cfg {boolean} striped Should the rows be alternative striped
6101  * @cfg {boolean} bordered Add borders to the table
6102  * @cfg {boolean} hover Add hover highlighting
6103  * @cfg {boolean} condensed Format condensed
6104  * @cfg {boolean} responsive Format condensed
6105  * @cfg {Boolean} loadMask (true|false) default false
6106  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6107  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6108  * @cfg {Boolean} rowSelection (true|false) default false
6109  * @cfg {Boolean} cellSelection (true|false) default false
6110  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6111  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6112  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6113  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6114  
6115  * 
6116  * @constructor
6117  * Create a new Table
6118  * @param {Object} config The config object
6119  */
6120
6121 Roo.bootstrap.Table = function(config){
6122     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6123     
6124   
6125     
6126     // BC...
6127     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6128     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6129     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6130     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6131     
6132     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6133     if (this.sm) {
6134         this.sm.grid = this;
6135         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6136         this.sm = this.selModel;
6137         this.sm.xmodule = this.xmodule || false;
6138     }
6139     
6140     if (this.cm && typeof(this.cm.config) == 'undefined') {
6141         this.colModel = new Roo.grid.ColumnModel(this.cm);
6142         this.cm = this.colModel;
6143         this.cm.xmodule = this.xmodule || false;
6144     }
6145     if (this.store) {
6146         this.store= Roo.factory(this.store, Roo.data);
6147         this.ds = this.store;
6148         this.ds.xmodule = this.xmodule || false;
6149          
6150     }
6151     if (this.footer && this.store) {
6152         this.footer.dataSource = this.ds;
6153         this.footer = Roo.factory(this.footer);
6154     }
6155     
6156     /** @private */
6157     this.addEvents({
6158         /**
6159          * @event cellclick
6160          * Fires when a cell is clicked
6161          * @param {Roo.bootstrap.Table} this
6162          * @param {Roo.Element} el
6163          * @param {Number} rowIndex
6164          * @param {Number} columnIndex
6165          * @param {Roo.EventObject} e
6166          */
6167         "cellclick" : true,
6168         /**
6169          * @event celldblclick
6170          * Fires when a cell is double clicked
6171          * @param {Roo.bootstrap.Table} this
6172          * @param {Roo.Element} el
6173          * @param {Number} rowIndex
6174          * @param {Number} columnIndex
6175          * @param {Roo.EventObject} e
6176          */
6177         "celldblclick" : true,
6178         /**
6179          * @event rowclick
6180          * Fires when a row is clicked
6181          * @param {Roo.bootstrap.Table} this
6182          * @param {Roo.Element} el
6183          * @param {Number} rowIndex
6184          * @param {Roo.EventObject} e
6185          */
6186         "rowclick" : true,
6187         /**
6188          * @event rowdblclick
6189          * Fires when a row is double clicked
6190          * @param {Roo.bootstrap.Table} this
6191          * @param {Roo.Element} el
6192          * @param {Number} rowIndex
6193          * @param {Roo.EventObject} e
6194          */
6195         "rowdblclick" : true,
6196         /**
6197          * @event mouseover
6198          * Fires when a mouseover occur
6199          * @param {Roo.bootstrap.Table} this
6200          * @param {Roo.Element} el
6201          * @param {Number} rowIndex
6202          * @param {Number} columnIndex
6203          * @param {Roo.EventObject} e
6204          */
6205         "mouseover" : true,
6206         /**
6207          * @event mouseout
6208          * Fires when a mouseout occur
6209          * @param {Roo.bootstrap.Table} this
6210          * @param {Roo.Element} el
6211          * @param {Number} rowIndex
6212          * @param {Number} columnIndex
6213          * @param {Roo.EventObject} e
6214          */
6215         "mouseout" : true,
6216         /**
6217          * @event rowclass
6218          * Fires when a row is rendered, so you can change add a style to it.
6219          * @param {Roo.bootstrap.Table} this
6220          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6221          */
6222         'rowclass' : true,
6223           /**
6224          * @event rowsrendered
6225          * Fires when all the  rows have been rendered
6226          * @param {Roo.bootstrap.Table} this
6227          */
6228         'rowsrendered' : true,
6229         /**
6230          * @event contextmenu
6231          * The raw contextmenu event for the entire grid.
6232          * @param {Roo.EventObject} e
6233          */
6234         "contextmenu" : true,
6235         /**
6236          * @event rowcontextmenu
6237          * Fires when a row is right clicked
6238          * @param {Roo.bootstrap.Table} this
6239          * @param {Number} rowIndex
6240          * @param {Roo.EventObject} e
6241          */
6242         "rowcontextmenu" : true,
6243         /**
6244          * @event cellcontextmenu
6245          * Fires when a cell is right clicked
6246          * @param {Roo.bootstrap.Table} this
6247          * @param {Number} rowIndex
6248          * @param {Number} cellIndex
6249          * @param {Roo.EventObject} e
6250          */
6251          "cellcontextmenu" : true,
6252          /**
6253          * @event headercontextmenu
6254          * Fires when a header is right clicked
6255          * @param {Roo.bootstrap.Table} this
6256          * @param {Number} columnIndex
6257          * @param {Roo.EventObject} e
6258          */
6259         "headercontextmenu" : true
6260     });
6261 };
6262
6263 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6264     
6265     cls: false,
6266     align: false,
6267     bgcolor: false,
6268     border: false,
6269     cellpadding: false,
6270     cellspacing: false,
6271     frame: false,
6272     rules: false,
6273     sortable: false,
6274     summary: false,
6275     width: false,
6276     striped : false,
6277     scrollBody : false,
6278     bordered: false,
6279     hover:  false,
6280     condensed : false,
6281     responsive : false,
6282     sm : false,
6283     cm : false,
6284     store : false,
6285     loadMask : false,
6286     footerShow : true,
6287     headerShow : true,
6288   
6289     rowSelection : false,
6290     cellSelection : false,
6291     layout : false,
6292     
6293     // Roo.Element - the tbody
6294     mainBody: false,
6295     // Roo.Element - thead element
6296     mainHead: false,
6297     
6298     container: false, // used by gridpanel...
6299     
6300     lazyLoad : false,
6301     
6302     CSS : Roo.util.CSS,
6303     
6304     auto_hide_footer : false,
6305     
6306     getAutoCreate : function()
6307     {
6308         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6309         
6310         cfg = {
6311             tag: 'table',
6312             cls : 'table',
6313             cn : []
6314         };
6315         if (this.scrollBody) {
6316             cfg.cls += ' table-body-fixed';
6317         }    
6318         if (this.striped) {
6319             cfg.cls += ' table-striped';
6320         }
6321         
6322         if (this.hover) {
6323             cfg.cls += ' table-hover';
6324         }
6325         if (this.bordered) {
6326             cfg.cls += ' table-bordered';
6327         }
6328         if (this.condensed) {
6329             cfg.cls += ' table-condensed';
6330         }
6331         if (this.responsive) {
6332             cfg.cls += ' table-responsive';
6333         }
6334         
6335         if (this.cls) {
6336             cfg.cls+=  ' ' +this.cls;
6337         }
6338         
6339         // this lot should be simplifed...
6340         var _t = this;
6341         var cp = [
6342             'align',
6343             'bgcolor',
6344             'border',
6345             'cellpadding',
6346             'cellspacing',
6347             'frame',
6348             'rules',
6349             'sortable',
6350             'summary',
6351             'width'
6352         ].forEach(function(k) {
6353             if (_t[k]) {
6354                 cfg[k] = _t[k];
6355             }
6356         });
6357         
6358         
6359         if (this.layout) {
6360             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6361         }
6362         
6363         if(this.store || this.cm){
6364             if(this.headerShow){
6365                 cfg.cn.push(this.renderHeader());
6366             }
6367             
6368             cfg.cn.push(this.renderBody());
6369             
6370             if(this.footerShow){
6371                 cfg.cn.push(this.renderFooter());
6372             }
6373             // where does this come from?
6374             //cfg.cls+=  ' TableGrid';
6375         }
6376         
6377         return { cn : [ cfg ] };
6378     },
6379     
6380     initEvents : function()
6381     {   
6382         if(!this.store || !this.cm){
6383             return;
6384         }
6385         if (this.selModel) {
6386             this.selModel.initEvents();
6387         }
6388         
6389         
6390         //Roo.log('initEvents with ds!!!!');
6391         
6392         this.mainBody = this.el.select('tbody', true).first();
6393         this.mainHead = this.el.select('thead', true).first();
6394         this.mainFoot = this.el.select('tfoot', true).first();
6395         
6396         
6397         
6398         var _this = this;
6399         
6400         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6401             e.on('click', _this.sort, _this);
6402         });
6403         
6404         this.mainBody.on("click", this.onClick, this);
6405         this.mainBody.on("dblclick", this.onDblClick, this);
6406         
6407         // why is this done????? = it breaks dialogs??
6408         //this.parent().el.setStyle('position', 'relative');
6409         
6410         
6411         if (this.footer) {
6412             this.footer.parentId = this.id;
6413             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6414             
6415             if(this.lazyLoad){
6416                 this.el.select('tfoot tr td').first().addClass('hide');
6417             }
6418         } 
6419         
6420         if(this.loadMask) {
6421             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6422         }
6423         
6424         this.store.on('load', this.onLoad, this);
6425         this.store.on('beforeload', this.onBeforeLoad, this);
6426         this.store.on('update', this.onUpdate, this);
6427         this.store.on('add', this.onAdd, this);
6428         this.store.on("clear", this.clear, this);
6429         
6430         this.el.on("contextmenu", this.onContextMenu, this);
6431         
6432         this.mainBody.on('scroll', this.onBodyScroll, this);
6433         
6434         this.cm.on("headerchange", this.onHeaderChange, this);
6435         
6436         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6437         
6438     },
6439     
6440     onContextMenu : function(e, t)
6441     {
6442         this.processEvent("contextmenu", e);
6443     },
6444     
6445     processEvent : function(name, e)
6446     {
6447         if (name != 'touchstart' ) {
6448             this.fireEvent(name, e);    
6449         }
6450         
6451         var t = e.getTarget();
6452         
6453         var cell = Roo.get(t);
6454         
6455         if(!cell){
6456             return;
6457         }
6458         
6459         if(cell.findParent('tfoot', false, true)){
6460             return;
6461         }
6462         
6463         if(cell.findParent('thead', false, true)){
6464             
6465             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6466                 cell = Roo.get(t).findParent('th', false, true);
6467                 if (!cell) {
6468                     Roo.log("failed to find th in thead?");
6469                     Roo.log(e.getTarget());
6470                     return;
6471                 }
6472             }
6473             
6474             var cellIndex = cell.dom.cellIndex;
6475             
6476             var ename = name == 'touchstart' ? 'click' : name;
6477             this.fireEvent("header" + ename, this, cellIndex, e);
6478             
6479             return;
6480         }
6481         
6482         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6483             cell = Roo.get(t).findParent('td', false, true);
6484             if (!cell) {
6485                 Roo.log("failed to find th in tbody?");
6486                 Roo.log(e.getTarget());
6487                 return;
6488             }
6489         }
6490         
6491         var row = cell.findParent('tr', false, true);
6492         var cellIndex = cell.dom.cellIndex;
6493         var rowIndex = row.dom.rowIndex - 1;
6494         
6495         if(row !== false){
6496             
6497             this.fireEvent("row" + name, this, rowIndex, e);
6498             
6499             if(cell !== false){
6500             
6501                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6502             }
6503         }
6504         
6505     },
6506     
6507     onMouseover : function(e, el)
6508     {
6509         var cell = Roo.get(el);
6510         
6511         if(!cell){
6512             return;
6513         }
6514         
6515         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6516             cell = cell.findParent('td', false, true);
6517         }
6518         
6519         var row = cell.findParent('tr', false, true);
6520         var cellIndex = cell.dom.cellIndex;
6521         var rowIndex = row.dom.rowIndex - 1; // start from 0
6522         
6523         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6524         
6525     },
6526     
6527     onMouseout : 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('mouseout', this, cell, rowIndex, cellIndex, e);
6544         
6545     },
6546     
6547     onClick : function(e, el)
6548     {
6549         var cell = Roo.get(el);
6550         
6551         if(!cell || (!this.cellSelection && !this.rowSelection)){
6552             return;
6553         }
6554         
6555         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6556             cell = cell.findParent('td', false, true);
6557         }
6558         
6559         if(!cell || typeof(cell) == 'undefined'){
6560             return;
6561         }
6562         
6563         var row = cell.findParent('tr', false, true);
6564         
6565         if(!row || typeof(row) == 'undefined'){
6566             return;
6567         }
6568         
6569         var cellIndex = cell.dom.cellIndex;
6570         var rowIndex = this.getRowIndex(row);
6571         
6572         // why??? - should these not be based on SelectionModel?
6573         if(this.cellSelection){
6574             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6575         }
6576         
6577         if(this.rowSelection){
6578             this.fireEvent('rowclick', this, row, rowIndex, e);
6579         }
6580         
6581         
6582     },
6583         
6584     onDblClick : function(e,el)
6585     {
6586         var cell = Roo.get(el);
6587         
6588         if(!cell || (!this.cellSelection && !this.rowSelection)){
6589             return;
6590         }
6591         
6592         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6593             cell = cell.findParent('td', false, true);
6594         }
6595         
6596         if(!cell || typeof(cell) == 'undefined'){
6597             return;
6598         }
6599         
6600         var row = cell.findParent('tr', false, true);
6601         
6602         if(!row || typeof(row) == 'undefined'){
6603             return;
6604         }
6605         
6606         var cellIndex = cell.dom.cellIndex;
6607         var rowIndex = this.getRowIndex(row);
6608         
6609         if(this.cellSelection){
6610             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6611         }
6612         
6613         if(this.rowSelection){
6614             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6615         }
6616     },
6617     
6618     sort : function(e,el)
6619     {
6620         var col = Roo.get(el);
6621         
6622         if(!col.hasClass('sortable')){
6623             return;
6624         }
6625         
6626         var sort = col.attr('sort');
6627         var dir = 'ASC';
6628         
6629         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6630             dir = 'DESC';
6631         }
6632         
6633         this.store.sortInfo = {field : sort, direction : dir};
6634         
6635         if (this.footer) {
6636             Roo.log("calling footer first");
6637             this.footer.onClick('first');
6638         } else {
6639         
6640             this.store.load({ params : { start : 0 } });
6641         }
6642     },
6643     
6644     renderHeader : function()
6645     {
6646         var header = {
6647             tag: 'thead',
6648             cn : []
6649         };
6650         
6651         var cm = this.cm;
6652         this.totalWidth = 0;
6653         
6654         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6655             
6656             var config = cm.config[i];
6657             
6658             var c = {
6659                 tag: 'th',
6660                 cls : 'x-hcol-' + i,
6661                 style : '',
6662                 html: cm.getColumnHeader(i)
6663             };
6664             
6665             var hh = '';
6666             
6667             if(typeof(config.sortable) != 'undefined' && config.sortable){
6668                 c.cls = 'sortable';
6669                 c.html = '<i class="glyphicon"></i>' + c.html;
6670             }
6671             
6672             if(typeof(config.lgHeader) != 'undefined'){
6673                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6674             }
6675             
6676             if(typeof(config.mdHeader) != 'undefined'){
6677                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6678             }
6679             
6680             if(typeof(config.smHeader) != 'undefined'){
6681                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6682             }
6683             
6684             if(typeof(config.xsHeader) != 'undefined'){
6685                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6686             }
6687             
6688             if(hh.length){
6689                 c.html = hh;
6690             }
6691             
6692             if(typeof(config.tooltip) != 'undefined'){
6693                 c.tooltip = config.tooltip;
6694             }
6695             
6696             if(typeof(config.colspan) != 'undefined'){
6697                 c.colspan = config.colspan;
6698             }
6699             
6700             if(typeof(config.hidden) != 'undefined' && config.hidden){
6701                 c.style += ' display:none;';
6702             }
6703             
6704             if(typeof(config.dataIndex) != 'undefined'){
6705                 c.sort = config.dataIndex;
6706             }
6707             
6708            
6709             
6710             if(typeof(config.align) != 'undefined' && config.align.length){
6711                 c.style += ' text-align:' + config.align + ';';
6712             }
6713             
6714             if(typeof(config.width) != 'undefined'){
6715                 c.style += ' width:' + config.width + 'px;';
6716                 this.totalWidth += config.width;
6717             } else {
6718                 this.totalWidth += 100; // assume minimum of 100 per column?
6719             }
6720             
6721             if(typeof(config.cls) != 'undefined'){
6722                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6723             }
6724             
6725             ['xs','sm','md','lg'].map(function(size){
6726                 
6727                 if(typeof(config[size]) == 'undefined'){
6728                     return;
6729                 }
6730                 
6731                 if (!config[size]) { // 0 = hidden
6732                     c.cls += ' hidden-' + size;
6733                     return;
6734                 }
6735                 
6736                 c.cls += ' col-' + size + '-' + config[size];
6737
6738             });
6739             
6740             header.cn.push(c)
6741         }
6742         
6743         return header;
6744     },
6745     
6746     renderBody : function()
6747     {
6748         var body = {
6749             tag: 'tbody',
6750             cn : [
6751                 {
6752                     tag: 'tr',
6753                     cn : [
6754                         {
6755                             tag : 'td',
6756                             colspan :  this.cm.getColumnCount()
6757                         }
6758                     ]
6759                 }
6760             ]
6761         };
6762         
6763         return body;
6764     },
6765     
6766     renderFooter : function()
6767     {
6768         var footer = {
6769             tag: 'tfoot',
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 footer;
6784     },
6785     
6786     
6787     
6788     onLoad : function()
6789     {
6790 //        Roo.log('ds onload');
6791         this.clear();
6792         
6793         var _this = this;
6794         var cm = this.cm;
6795         var ds = this.store;
6796         
6797         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6798             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6799             if (_this.store.sortInfo) {
6800                     
6801                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6802                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6803                 }
6804                 
6805                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6806                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6807                 }
6808             }
6809         });
6810         
6811         var tbody =  this.mainBody;
6812               
6813         if(ds.getCount() > 0){
6814             ds.data.each(function(d,rowIndex){
6815                 var row =  this.renderRow(cm, ds, rowIndex);
6816                 
6817                 tbody.createChild(row);
6818                 
6819                 var _this = this;
6820                 
6821                 if(row.cellObjects.length){
6822                     Roo.each(row.cellObjects, function(r){
6823                         _this.renderCellObject(r);
6824                     })
6825                 }
6826                 
6827             }, this);
6828         }
6829         
6830         var tfoot = this.el.select('tfoot', true).first();
6831         
6832         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6833             
6834             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6835             
6836             var total = this.ds.getTotalCount();
6837             
6838             if(this.footer.pageSize < total){
6839                 this.mainFoot.show();
6840             }
6841         }
6842         
6843         Roo.each(this.el.select('tbody td', true).elements, function(e){
6844             e.on('mouseover', _this.onMouseover, _this);
6845         });
6846         
6847         Roo.each(this.el.select('tbody td', true).elements, function(e){
6848             e.on('mouseout', _this.onMouseout, _this);
6849         });
6850         this.fireEvent('rowsrendered', this);
6851         
6852         this.autoSize();
6853     },
6854     
6855     
6856     onUpdate : function(ds,record)
6857     {
6858         this.refreshRow(record);
6859         this.autoSize();
6860     },
6861     
6862     onRemove : function(ds, record, index, isUpdate){
6863         if(isUpdate !== true){
6864             this.fireEvent("beforerowremoved", this, index, record);
6865         }
6866         var bt = this.mainBody.dom;
6867         
6868         var rows = this.el.select('tbody > tr', true).elements;
6869         
6870         if(typeof(rows[index]) != 'undefined'){
6871             bt.removeChild(rows[index].dom);
6872         }
6873         
6874 //        if(bt.rows[index]){
6875 //            bt.removeChild(bt.rows[index]);
6876 //        }
6877         
6878         if(isUpdate !== true){
6879             //this.stripeRows(index);
6880             //this.syncRowHeights(index, index);
6881             //this.layout();
6882             this.fireEvent("rowremoved", this, index, record);
6883         }
6884     },
6885     
6886     onAdd : function(ds, records, rowIndex)
6887     {
6888         //Roo.log('on Add called');
6889         // - note this does not handle multiple adding very well..
6890         var bt = this.mainBody.dom;
6891         for (var i =0 ; i < records.length;i++) {
6892             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6893             //Roo.log(records[i]);
6894             //Roo.log(this.store.getAt(rowIndex+i));
6895             this.insertRow(this.store, rowIndex + i, false);
6896             return;
6897         }
6898         
6899     },
6900     
6901     
6902     refreshRow : function(record){
6903         var ds = this.store, index;
6904         if(typeof record == 'number'){
6905             index = record;
6906             record = ds.getAt(index);
6907         }else{
6908             index = ds.indexOf(record);
6909         }
6910         this.insertRow(ds, index, true);
6911         this.autoSize();
6912         this.onRemove(ds, record, index+1, true);
6913         this.autoSize();
6914         //this.syncRowHeights(index, index);
6915         //this.layout();
6916         this.fireEvent("rowupdated", this, index, record);
6917     },
6918     
6919     insertRow : function(dm, rowIndex, isUpdate){
6920         
6921         if(!isUpdate){
6922             this.fireEvent("beforerowsinserted", this, rowIndex);
6923         }
6924             //var s = this.getScrollState();
6925         var row = this.renderRow(this.cm, this.store, rowIndex);
6926         // insert before rowIndex..
6927         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6928         
6929         var _this = this;
6930                 
6931         if(row.cellObjects.length){
6932             Roo.each(row.cellObjects, function(r){
6933                 _this.renderCellObject(r);
6934             })
6935         }
6936             
6937         if(!isUpdate){
6938             this.fireEvent("rowsinserted", this, rowIndex);
6939             //this.syncRowHeights(firstRow, lastRow);
6940             //this.stripeRows(firstRow);
6941             //this.layout();
6942         }
6943         
6944     },
6945     
6946     
6947     getRowDom : function(rowIndex)
6948     {
6949         var rows = this.el.select('tbody > tr', true).elements;
6950         
6951         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6952         
6953     },
6954     // returns the object tree for a tr..
6955   
6956     
6957     renderRow : function(cm, ds, rowIndex) 
6958     {
6959         var d = ds.getAt(rowIndex);
6960         
6961         var row = {
6962             tag : 'tr',
6963             cls : 'x-row-' + rowIndex,
6964             cn : []
6965         };
6966             
6967         var cellObjects = [];
6968         
6969         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6970             var config = cm.config[i];
6971             
6972             var renderer = cm.getRenderer(i);
6973             var value = '';
6974             var id = false;
6975             
6976             if(typeof(renderer) !== 'undefined'){
6977                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6978             }
6979             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6980             // and are rendered into the cells after the row is rendered - using the id for the element.
6981             
6982             if(typeof(value) === 'object'){
6983                 id = Roo.id();
6984                 cellObjects.push({
6985                     container : id,
6986                     cfg : value 
6987                 })
6988             }
6989             
6990             var rowcfg = {
6991                 record: d,
6992                 rowIndex : rowIndex,
6993                 colIndex : i,
6994                 rowClass : ''
6995             };
6996
6997             this.fireEvent('rowclass', this, rowcfg);
6998             
6999             var td = {
7000                 tag: 'td',
7001                 cls : rowcfg.rowClass + ' x-col-' + i,
7002                 style: '',
7003                 html: (typeof(value) === 'object') ? '' : value
7004             };
7005             
7006             if (id) {
7007                 td.id = id;
7008             }
7009             
7010             if(typeof(config.colspan) != 'undefined'){
7011                 td.colspan = config.colspan;
7012             }
7013             
7014             if(typeof(config.hidden) != 'undefined' && config.hidden){
7015                 td.style += ' display:none;';
7016             }
7017             
7018             if(typeof(config.align) != 'undefined' && config.align.length){
7019                 td.style += ' text-align:' + config.align + ';';
7020             }
7021             if(typeof(config.valign) != 'undefined' && config.valign.length){
7022                 td.style += ' vertical-align:' + config.valign + ';';
7023             }
7024             
7025             if(typeof(config.width) != 'undefined'){
7026                 td.style += ' width:' +  config.width + 'px;';
7027             }
7028             
7029             if(typeof(config.cursor) != 'undefined'){
7030                 td.style += ' cursor:' +  config.cursor + ';';
7031             }
7032             
7033             if(typeof(config.cls) != 'undefined'){
7034                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7035             }
7036             
7037             ['xs','sm','md','lg'].map(function(size){
7038                 
7039                 if(typeof(config[size]) == 'undefined'){
7040                     return;
7041                 }
7042                 
7043                 if (!config[size]) { // 0 = hidden
7044                     td.cls += ' hidden-' + size;
7045                     return;
7046                 }
7047                 
7048                 td.cls += ' col-' + size + '-' + config[size];
7049
7050             });
7051             
7052             row.cn.push(td);
7053            
7054         }
7055         
7056         row.cellObjects = cellObjects;
7057         
7058         return row;
7059           
7060     },
7061     
7062     
7063     
7064     onBeforeLoad : function()
7065     {
7066         
7067     },
7068      /**
7069      * Remove all rows
7070      */
7071     clear : function()
7072     {
7073         this.el.select('tbody', true).first().dom.innerHTML = '';
7074     },
7075     /**
7076      * Show or hide a row.
7077      * @param {Number} rowIndex to show or hide
7078      * @param {Boolean} state hide
7079      */
7080     setRowVisibility : function(rowIndex, state)
7081     {
7082         var bt = this.mainBody.dom;
7083         
7084         var rows = this.el.select('tbody > tr', true).elements;
7085         
7086         if(typeof(rows[rowIndex]) == 'undefined'){
7087             return;
7088         }
7089         rows[rowIndex].dom.style.display = state ? '' : 'none';
7090     },
7091     
7092     
7093     getSelectionModel : function(){
7094         if(!this.selModel){
7095             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7096         }
7097         return this.selModel;
7098     },
7099     /*
7100      * Render the Roo.bootstrap object from renderder
7101      */
7102     renderCellObject : function(r)
7103     {
7104         var _this = this;
7105         
7106         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7107         
7108         var t = r.cfg.render(r.container);
7109         
7110         if(r.cfg.cn){
7111             Roo.each(r.cfg.cn, function(c){
7112                 var child = {
7113                     container: t.getChildContainer(),
7114                     cfg: c
7115                 };
7116                 _this.renderCellObject(child);
7117             })
7118         }
7119     },
7120     
7121     getRowIndex : function(row)
7122     {
7123         var rowIndex = -1;
7124         
7125         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7126             if(el != row){
7127                 return;
7128             }
7129             
7130             rowIndex = index;
7131         });
7132         
7133         return rowIndex;
7134     },
7135      /**
7136      * Returns the grid's underlying element = used by panel.Grid
7137      * @return {Element} The element
7138      */
7139     getGridEl : function(){
7140         return this.el;
7141     },
7142      /**
7143      * Forces a resize - used by panel.Grid
7144      * @return {Element} The element
7145      */
7146     autoSize : function()
7147     {
7148         //var ctr = Roo.get(this.container.dom.parentElement);
7149         var ctr = Roo.get(this.el.dom);
7150         
7151         var thd = this.getGridEl().select('thead',true).first();
7152         var tbd = this.getGridEl().select('tbody', true).first();
7153         var tfd = this.getGridEl().select('tfoot', true).first();
7154         
7155         var cw = ctr.getWidth();
7156         
7157         if (tbd) {
7158             
7159             tbd.setSize(ctr.getWidth(),
7160                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7161             );
7162             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7163             cw -= barsize;
7164         }
7165         cw = Math.max(cw, this.totalWidth);
7166         this.getGridEl().select('tr',true).setWidth(cw);
7167         // resize 'expandable coloumn?
7168         
7169         return; // we doe not have a view in this design..
7170         
7171     },
7172     onBodyScroll: function()
7173     {
7174         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7175         if(this.mainHead){
7176             this.mainHead.setStyle({
7177                 'position' : 'relative',
7178                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7179             });
7180         }
7181         
7182         if(this.lazyLoad){
7183             
7184             var scrollHeight = this.mainBody.dom.scrollHeight;
7185             
7186             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7187             
7188             var height = this.mainBody.getHeight();
7189             
7190             if(scrollHeight - height == scrollTop) {
7191                 
7192                 var total = this.ds.getTotalCount();
7193                 
7194                 if(this.footer.cursor + this.footer.pageSize < total){
7195                     
7196                     this.footer.ds.load({
7197                         params : {
7198                             start : this.footer.cursor + this.footer.pageSize,
7199                             limit : this.footer.pageSize
7200                         },
7201                         add : true
7202                     });
7203                 }
7204             }
7205             
7206         }
7207     },
7208     
7209     onHeaderChange : function()
7210     {
7211         var header = this.renderHeader();
7212         var table = this.el.select('table', true).first();
7213         
7214         this.mainHead.remove();
7215         this.mainHead = table.createChild(header, this.mainBody, false);
7216     },
7217     
7218     onHiddenChange : function(colModel, colIndex, hidden)
7219     {
7220         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7221         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7222         
7223         this.CSS.updateRule(thSelector, "display", "");
7224         this.CSS.updateRule(tdSelector, "display", "");
7225         
7226         if(hidden){
7227             this.CSS.updateRule(thSelector, "display", "none");
7228             this.CSS.updateRule(tdSelector, "display", "none");
7229         }
7230         
7231         this.onHeaderChange();
7232         this.onLoad();
7233     },
7234     
7235     setColumnWidth: function(col_index, width)
7236     {
7237         // width = "md-2 xs-2..."
7238         if(!this.colModel.config[col_index]) {
7239             return;
7240         }
7241         
7242         var w = width.split(" ");
7243         
7244         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7245         
7246         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7247         
7248         
7249         for(var j = 0; j < w.length; j++) {
7250             
7251             if(!w[j]) {
7252                 continue;
7253             }
7254             
7255             var size_cls = w[j].split("-");
7256             
7257             if(!Number.isInteger(size_cls[1] * 1)) {
7258                 continue;
7259             }
7260             
7261             if(!this.colModel.config[col_index][size_cls[0]]) {
7262                 continue;
7263             }
7264             
7265             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7266                 continue;
7267             }
7268             
7269             h_row[0].classList.replace(
7270                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7271                 "col-"+size_cls[0]+"-"+size_cls[1]
7272             );
7273             
7274             for(var i = 0; i < rows.length; i++) {
7275                 
7276                 var size_cls = w[j].split("-");
7277                 
7278                 if(!Number.isInteger(size_cls[1] * 1)) {
7279                     continue;
7280                 }
7281                 
7282                 if(!this.colModel.config[col_index][size_cls[0]]) {
7283                     continue;
7284                 }
7285                 
7286                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7287                     continue;
7288                 }
7289                 
7290                 rows[i].classList.replace(
7291                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7292                     "col-"+size_cls[0]+"-"+size_cls[1]
7293                 );
7294             }
7295             
7296             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7297         }
7298     }
7299 });
7300
7301  
7302
7303  /*
7304  * - LGPL
7305  *
7306  * table cell
7307  * 
7308  */
7309
7310 /**
7311  * @class Roo.bootstrap.TableCell
7312  * @extends Roo.bootstrap.Component
7313  * Bootstrap TableCell class
7314  * @cfg {String} html cell contain text
7315  * @cfg {String} cls cell class
7316  * @cfg {String} tag cell tag (td|th) default td
7317  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7318  * @cfg {String} align Aligns the content in a cell
7319  * @cfg {String} axis Categorizes cells
7320  * @cfg {String} bgcolor Specifies the background color of a cell
7321  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7322  * @cfg {Number} colspan Specifies the number of columns a cell should span
7323  * @cfg {String} headers Specifies one or more header cells a cell is related to
7324  * @cfg {Number} height Sets the height of a cell
7325  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7326  * @cfg {Number} rowspan Sets the number of rows a cell should span
7327  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7328  * @cfg {String} valign Vertical aligns the content in a cell
7329  * @cfg {Number} width Specifies the width of a cell
7330  * 
7331  * @constructor
7332  * Create a new TableCell
7333  * @param {Object} config The config object
7334  */
7335
7336 Roo.bootstrap.TableCell = function(config){
7337     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7338 };
7339
7340 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7341     
7342     html: false,
7343     cls: false,
7344     tag: false,
7345     abbr: false,
7346     align: false,
7347     axis: false,
7348     bgcolor: false,
7349     charoff: false,
7350     colspan: false,
7351     headers: false,
7352     height: false,
7353     nowrap: false,
7354     rowspan: false,
7355     scope: false,
7356     valign: false,
7357     width: false,
7358     
7359     
7360     getAutoCreate : function(){
7361         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7362         
7363         cfg = {
7364             tag: 'td'
7365         };
7366         
7367         if(this.tag){
7368             cfg.tag = this.tag;
7369         }
7370         
7371         if (this.html) {
7372             cfg.html=this.html
7373         }
7374         if (this.cls) {
7375             cfg.cls=this.cls
7376         }
7377         if (this.abbr) {
7378             cfg.abbr=this.abbr
7379         }
7380         if (this.align) {
7381             cfg.align=this.align
7382         }
7383         if (this.axis) {
7384             cfg.axis=this.axis
7385         }
7386         if (this.bgcolor) {
7387             cfg.bgcolor=this.bgcolor
7388         }
7389         if (this.charoff) {
7390             cfg.charoff=this.charoff
7391         }
7392         if (this.colspan) {
7393             cfg.colspan=this.colspan
7394         }
7395         if (this.headers) {
7396             cfg.headers=this.headers
7397         }
7398         if (this.height) {
7399             cfg.height=this.height
7400         }
7401         if (this.nowrap) {
7402             cfg.nowrap=this.nowrap
7403         }
7404         if (this.rowspan) {
7405             cfg.rowspan=this.rowspan
7406         }
7407         if (this.scope) {
7408             cfg.scope=this.scope
7409         }
7410         if (this.valign) {
7411             cfg.valign=this.valign
7412         }
7413         if (this.width) {
7414             cfg.width=this.width
7415         }
7416         
7417         
7418         return cfg;
7419     }
7420    
7421 });
7422
7423  
7424
7425  /*
7426  * - LGPL
7427  *
7428  * table row
7429  * 
7430  */
7431
7432 /**
7433  * @class Roo.bootstrap.TableRow
7434  * @extends Roo.bootstrap.Component
7435  * Bootstrap TableRow class
7436  * @cfg {String} cls row class
7437  * @cfg {String} align Aligns the content in a table row
7438  * @cfg {String} bgcolor Specifies a background color for a table row
7439  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7440  * @cfg {String} valign Vertical aligns the content in a table row
7441  * 
7442  * @constructor
7443  * Create a new TableRow
7444  * @param {Object} config The config object
7445  */
7446
7447 Roo.bootstrap.TableRow = function(config){
7448     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7449 };
7450
7451 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7452     
7453     cls: false,
7454     align: false,
7455     bgcolor: false,
7456     charoff: false,
7457     valign: false,
7458     
7459     getAutoCreate : function(){
7460         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7461         
7462         cfg = {
7463             tag: 'tr'
7464         };
7465             
7466         if(this.cls){
7467             cfg.cls = this.cls;
7468         }
7469         if(this.align){
7470             cfg.align = this.align;
7471         }
7472         if(this.bgcolor){
7473             cfg.bgcolor = this.bgcolor;
7474         }
7475         if(this.charoff){
7476             cfg.charoff = this.charoff;
7477         }
7478         if(this.valign){
7479             cfg.valign = this.valign;
7480         }
7481         
7482         return cfg;
7483     }
7484    
7485 });
7486
7487  
7488
7489  /*
7490  * - LGPL
7491  *
7492  * table body
7493  * 
7494  */
7495
7496 /**
7497  * @class Roo.bootstrap.TableBody
7498  * @extends Roo.bootstrap.Component
7499  * Bootstrap TableBody class
7500  * @cfg {String} cls element class
7501  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7502  * @cfg {String} align Aligns the content inside the element
7503  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7504  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7505  * 
7506  * @constructor
7507  * Create a new TableBody
7508  * @param {Object} config The config object
7509  */
7510
7511 Roo.bootstrap.TableBody = function(config){
7512     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7513 };
7514
7515 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7516     
7517     cls: false,
7518     tag: false,
7519     align: false,
7520     charoff: false,
7521     valign: false,
7522     
7523     getAutoCreate : function(){
7524         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7525         
7526         cfg = {
7527             tag: 'tbody'
7528         };
7529             
7530         if (this.cls) {
7531             cfg.cls=this.cls
7532         }
7533         if(this.tag){
7534             cfg.tag = this.tag;
7535         }
7536         
7537         if(this.align){
7538             cfg.align = this.align;
7539         }
7540         if(this.charoff){
7541             cfg.charoff = this.charoff;
7542         }
7543         if(this.valign){
7544             cfg.valign = this.valign;
7545         }
7546         
7547         return cfg;
7548     }
7549     
7550     
7551 //    initEvents : function()
7552 //    {
7553 //        
7554 //        if(!this.store){
7555 //            return;
7556 //        }
7557 //        
7558 //        this.store = Roo.factory(this.store, Roo.data);
7559 //        this.store.on('load', this.onLoad, this);
7560 //        
7561 //        this.store.load();
7562 //        
7563 //    },
7564 //    
7565 //    onLoad: function () 
7566 //    {   
7567 //        this.fireEvent('load', this);
7568 //    }
7569 //    
7570 //   
7571 });
7572
7573  
7574
7575  /*
7576  * Based on:
7577  * Ext JS Library 1.1.1
7578  * Copyright(c) 2006-2007, Ext JS, LLC.
7579  *
7580  * Originally Released Under LGPL - original licence link has changed is not relivant.
7581  *
7582  * Fork - LGPL
7583  * <script type="text/javascript">
7584  */
7585
7586 // as we use this in bootstrap.
7587 Roo.namespace('Roo.form');
7588  /**
7589  * @class Roo.form.Action
7590  * Internal Class used to handle form actions
7591  * @constructor
7592  * @param {Roo.form.BasicForm} el The form element or its id
7593  * @param {Object} config Configuration options
7594  */
7595
7596  
7597  
7598 // define the action interface
7599 Roo.form.Action = function(form, options){
7600     this.form = form;
7601     this.options = options || {};
7602 };
7603 /**
7604  * Client Validation Failed
7605  * @const 
7606  */
7607 Roo.form.Action.CLIENT_INVALID = 'client';
7608 /**
7609  * Server Validation Failed
7610  * @const 
7611  */
7612 Roo.form.Action.SERVER_INVALID = 'server';
7613  /**
7614  * Connect to Server Failed
7615  * @const 
7616  */
7617 Roo.form.Action.CONNECT_FAILURE = 'connect';
7618 /**
7619  * Reading Data from Server Failed
7620  * @const 
7621  */
7622 Roo.form.Action.LOAD_FAILURE = 'load';
7623
7624 Roo.form.Action.prototype = {
7625     type : 'default',
7626     failureType : undefined,
7627     response : undefined,
7628     result : undefined,
7629
7630     // interface method
7631     run : function(options){
7632
7633     },
7634
7635     // interface method
7636     success : function(response){
7637
7638     },
7639
7640     // interface method
7641     handleResponse : function(response){
7642
7643     },
7644
7645     // default connection failure
7646     failure : function(response){
7647         
7648         this.response = response;
7649         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7650         this.form.afterAction(this, false);
7651     },
7652
7653     processResponse : function(response){
7654         this.response = response;
7655         if(!response.responseText){
7656             return true;
7657         }
7658         this.result = this.handleResponse(response);
7659         return this.result;
7660     },
7661
7662     // utility functions used internally
7663     getUrl : function(appendParams){
7664         var url = this.options.url || this.form.url || this.form.el.dom.action;
7665         if(appendParams){
7666             var p = this.getParams();
7667             if(p){
7668                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7669             }
7670         }
7671         return url;
7672     },
7673
7674     getMethod : function(){
7675         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7676     },
7677
7678     getParams : function(){
7679         var bp = this.form.baseParams;
7680         var p = this.options.params;
7681         if(p){
7682             if(typeof p == "object"){
7683                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7684             }else if(typeof p == 'string' && bp){
7685                 p += '&' + Roo.urlEncode(bp);
7686             }
7687         }else if(bp){
7688             p = Roo.urlEncode(bp);
7689         }
7690         return p;
7691     },
7692
7693     createCallback : function(){
7694         return {
7695             success: this.success,
7696             failure: this.failure,
7697             scope: this,
7698             timeout: (this.form.timeout*1000),
7699             upload: this.form.fileUpload ? this.success : undefined
7700         };
7701     }
7702 };
7703
7704 Roo.form.Action.Submit = function(form, options){
7705     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7706 };
7707
7708 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7709     type : 'submit',
7710
7711     haveProgress : false,
7712     uploadComplete : false,
7713     
7714     // uploadProgress indicator.
7715     uploadProgress : function()
7716     {
7717         if (!this.form.progressUrl) {
7718             return;
7719         }
7720         
7721         if (!this.haveProgress) {
7722             Roo.MessageBox.progress("Uploading", "Uploading");
7723         }
7724         if (this.uploadComplete) {
7725            Roo.MessageBox.hide();
7726            return;
7727         }
7728         
7729         this.haveProgress = true;
7730    
7731         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7732         
7733         var c = new Roo.data.Connection();
7734         c.request({
7735             url : this.form.progressUrl,
7736             params: {
7737                 id : uid
7738             },
7739             method: 'GET',
7740             success : function(req){
7741                //console.log(data);
7742                 var rdata = false;
7743                 var edata;
7744                 try  {
7745                    rdata = Roo.decode(req.responseText)
7746                 } catch (e) {
7747                     Roo.log("Invalid data from server..");
7748                     Roo.log(edata);
7749                     return;
7750                 }
7751                 if (!rdata || !rdata.success) {
7752                     Roo.log(rdata);
7753                     Roo.MessageBox.alert(Roo.encode(rdata));
7754                     return;
7755                 }
7756                 var data = rdata.data;
7757                 
7758                 if (this.uploadComplete) {
7759                    Roo.MessageBox.hide();
7760                    return;
7761                 }
7762                    
7763                 if (data){
7764                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7765                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7766                     );
7767                 }
7768                 this.uploadProgress.defer(2000,this);
7769             },
7770        
7771             failure: function(data) {
7772                 Roo.log('progress url failed ');
7773                 Roo.log(data);
7774             },
7775             scope : this
7776         });
7777            
7778     },
7779     
7780     
7781     run : function()
7782     {
7783         // run get Values on the form, so it syncs any secondary forms.
7784         this.form.getValues();
7785         
7786         var o = this.options;
7787         var method = this.getMethod();
7788         var isPost = method == 'POST';
7789         if(o.clientValidation === false || this.form.isValid()){
7790             
7791             if (this.form.progressUrl) {
7792                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7793                     (new Date() * 1) + '' + Math.random());
7794                     
7795             } 
7796             
7797             
7798             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7799                 form:this.form.el.dom,
7800                 url:this.getUrl(!isPost),
7801                 method: method,
7802                 params:isPost ? this.getParams() : null,
7803                 isUpload: this.form.fileUpload
7804             }));
7805             
7806             this.uploadProgress();
7807
7808         }else if (o.clientValidation !== false){ // client validation failed
7809             this.failureType = Roo.form.Action.CLIENT_INVALID;
7810             this.form.afterAction(this, false);
7811         }
7812     },
7813
7814     success : function(response)
7815     {
7816         this.uploadComplete= true;
7817         if (this.haveProgress) {
7818             Roo.MessageBox.hide();
7819         }
7820         
7821         
7822         var result = this.processResponse(response);
7823         if(result === true || result.success){
7824             this.form.afterAction(this, true);
7825             return;
7826         }
7827         if(result.errors){
7828             this.form.markInvalid(result.errors);
7829             this.failureType = Roo.form.Action.SERVER_INVALID;
7830         }
7831         this.form.afterAction(this, false);
7832     },
7833     failure : function(response)
7834     {
7835         this.uploadComplete= true;
7836         if (this.haveProgress) {
7837             Roo.MessageBox.hide();
7838         }
7839         
7840         this.response = response;
7841         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7842         this.form.afterAction(this, false);
7843     },
7844     
7845     handleResponse : function(response){
7846         if(this.form.errorReader){
7847             var rs = this.form.errorReader.read(response);
7848             var errors = [];
7849             if(rs.records){
7850                 for(var i = 0, len = rs.records.length; i < len; i++) {
7851                     var r = rs.records[i];
7852                     errors[i] = r.data;
7853                 }
7854             }
7855             if(errors.length < 1){
7856                 errors = null;
7857             }
7858             return {
7859                 success : rs.success,
7860                 errors : errors
7861             };
7862         }
7863         var ret = false;
7864         try {
7865             ret = Roo.decode(response.responseText);
7866         } catch (e) {
7867             ret = {
7868                 success: false,
7869                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7870                 errors : []
7871             };
7872         }
7873         return ret;
7874         
7875     }
7876 });
7877
7878
7879 Roo.form.Action.Load = function(form, options){
7880     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7881     this.reader = this.form.reader;
7882 };
7883
7884 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7885     type : 'load',
7886
7887     run : function(){
7888         
7889         Roo.Ajax.request(Roo.apply(
7890                 this.createCallback(), {
7891                     method:this.getMethod(),
7892                     url:this.getUrl(false),
7893                     params:this.getParams()
7894         }));
7895     },
7896
7897     success : function(response){
7898         
7899         var result = this.processResponse(response);
7900         if(result === true || !result.success || !result.data){
7901             this.failureType = Roo.form.Action.LOAD_FAILURE;
7902             this.form.afterAction(this, false);
7903             return;
7904         }
7905         this.form.clearInvalid();
7906         this.form.setValues(result.data);
7907         this.form.afterAction(this, true);
7908     },
7909
7910     handleResponse : function(response){
7911         if(this.form.reader){
7912             var rs = this.form.reader.read(response);
7913             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7914             return {
7915                 success : rs.success,
7916                 data : data
7917             };
7918         }
7919         return Roo.decode(response.responseText);
7920     }
7921 });
7922
7923 Roo.form.Action.ACTION_TYPES = {
7924     'load' : Roo.form.Action.Load,
7925     'submit' : Roo.form.Action.Submit
7926 };/*
7927  * - LGPL
7928  *
7929  * form
7930  *
7931  */
7932
7933 /**
7934  * @class Roo.bootstrap.Form
7935  * @extends Roo.bootstrap.Component
7936  * Bootstrap Form class
7937  * @cfg {String} method  GET | POST (default POST)
7938  * @cfg {String} labelAlign top | left (default top)
7939  * @cfg {String} align left  | right - for navbars
7940  * @cfg {Boolean} loadMask load mask when submit (default true)
7941
7942  *
7943  * @constructor
7944  * Create a new Form
7945  * @param {Object} config The config object
7946  */
7947
7948
7949 Roo.bootstrap.Form = function(config){
7950     
7951     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7952     
7953     Roo.bootstrap.Form.popover.apply();
7954     
7955     this.addEvents({
7956         /**
7957          * @event clientvalidation
7958          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7959          * @param {Form} this
7960          * @param {Boolean} valid true if the form has passed client-side validation
7961          */
7962         clientvalidation: true,
7963         /**
7964          * @event beforeaction
7965          * Fires before any action is performed. Return false to cancel the action.
7966          * @param {Form} this
7967          * @param {Action} action The action to be performed
7968          */
7969         beforeaction: true,
7970         /**
7971          * @event actionfailed
7972          * Fires when an action fails.
7973          * @param {Form} this
7974          * @param {Action} action The action that failed
7975          */
7976         actionfailed : true,
7977         /**
7978          * @event actioncomplete
7979          * Fires when an action is completed.
7980          * @param {Form} this
7981          * @param {Action} action The action that completed
7982          */
7983         actioncomplete : true
7984     });
7985 };
7986
7987 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7988
7989      /**
7990      * @cfg {String} method
7991      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7992      */
7993     method : 'POST',
7994     /**
7995      * @cfg {String} url
7996      * The URL to use for form actions if one isn't supplied in the action options.
7997      */
7998     /**
7999      * @cfg {Boolean} fileUpload
8000      * Set to true if this form is a file upload.
8001      */
8002
8003     /**
8004      * @cfg {Object} baseParams
8005      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8006      */
8007
8008     /**
8009      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8010      */
8011     timeout: 30,
8012     /**
8013      * @cfg {Sting} align (left|right) for navbar forms
8014      */
8015     align : 'left',
8016
8017     // private
8018     activeAction : null,
8019
8020     /**
8021      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8022      * element by passing it or its id or mask the form itself by passing in true.
8023      * @type Mixed
8024      */
8025     waitMsgTarget : false,
8026
8027     loadMask : true,
8028     
8029     /**
8030      * @cfg {Boolean} errorMask (true|false) default false
8031      */
8032     errorMask : false,
8033     
8034     /**
8035      * @cfg {Number} maskOffset Default 100
8036      */
8037     maskOffset : 100,
8038     
8039     /**
8040      * @cfg {Boolean} maskBody
8041      */
8042     maskBody : false,
8043
8044     getAutoCreate : function(){
8045
8046         var cfg = {
8047             tag: 'form',
8048             method : this.method || 'POST',
8049             id : this.id || Roo.id(),
8050             cls : ''
8051         };
8052         if (this.parent().xtype.match(/^Nav/)) {
8053             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8054
8055         }
8056
8057         if (this.labelAlign == 'left' ) {
8058             cfg.cls += ' form-horizontal';
8059         }
8060
8061
8062         return cfg;
8063     },
8064     initEvents : function()
8065     {
8066         this.el.on('submit', this.onSubmit, this);
8067         // this was added as random key presses on the form where triggering form submit.
8068         this.el.on('keypress', function(e) {
8069             if (e.getCharCode() != 13) {
8070                 return true;
8071             }
8072             // we might need to allow it for textareas.. and some other items.
8073             // check e.getTarget().
8074
8075             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8076                 return true;
8077             }
8078
8079             Roo.log("keypress blocked");
8080
8081             e.preventDefault();
8082             return false;
8083         });
8084         
8085     },
8086     // private
8087     onSubmit : function(e){
8088         e.stopEvent();
8089     },
8090
8091      /**
8092      * Returns true if client-side validation on the form is successful.
8093      * @return Boolean
8094      */
8095     isValid : function(){
8096         var items = this.getItems();
8097         var valid = true;
8098         var target = false;
8099         
8100         items.each(function(f){
8101             
8102             if(f.validate()){
8103                 return;
8104             }
8105             
8106             Roo.log('invalid field: ' + f.name);
8107             
8108             valid = false;
8109
8110             if(!target && f.el.isVisible(true)){
8111                 target = f;
8112             }
8113            
8114         });
8115         
8116         if(this.errorMask && !valid){
8117             Roo.bootstrap.Form.popover.mask(this, target);
8118         }
8119         
8120         return valid;
8121     },
8122     
8123     /**
8124      * Returns true if any fields in this form have changed since their original load.
8125      * @return Boolean
8126      */
8127     isDirty : function(){
8128         var dirty = false;
8129         var items = this.getItems();
8130         items.each(function(f){
8131            if(f.isDirty()){
8132                dirty = true;
8133                return false;
8134            }
8135            return true;
8136         });
8137         return dirty;
8138     },
8139      /**
8140      * Performs a predefined action (submit or load) or custom actions you define on this form.
8141      * @param {String} actionName The name of the action type
8142      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8143      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8144      * accept other config options):
8145      * <pre>
8146 Property          Type             Description
8147 ----------------  ---------------  ----------------------------------------------------------------------------------
8148 url               String           The url for the action (defaults to the form's url)
8149 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8150 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8151 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8152                                    validate the form on the client (defaults to false)
8153      * </pre>
8154      * @return {BasicForm} this
8155      */
8156     doAction : function(action, options){
8157         if(typeof action == 'string'){
8158             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8159         }
8160         if(this.fireEvent('beforeaction', this, action) !== false){
8161             this.beforeAction(action);
8162             action.run.defer(100, action);
8163         }
8164         return this;
8165     },
8166
8167     // private
8168     beforeAction : function(action){
8169         var o = action.options;
8170         
8171         if(this.loadMask){
8172             
8173             if(this.maskBody){
8174                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8175             } else {
8176                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8177             }
8178         }
8179         // not really supported yet.. ??
8180
8181         //if(this.waitMsgTarget === true){
8182         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8183         //}else if(this.waitMsgTarget){
8184         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8185         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8186         //}else {
8187         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8188        // }
8189
8190     },
8191
8192     // private
8193     afterAction : function(action, success){
8194         this.activeAction = null;
8195         var o = action.options;
8196
8197         if(this.loadMask){
8198             
8199             if(this.maskBody){
8200                 Roo.get(document.body).unmask();
8201             } else {
8202                 this.el.unmask();
8203             }
8204         }
8205         
8206         //if(this.waitMsgTarget === true){
8207 //            this.el.unmask();
8208         //}else if(this.waitMsgTarget){
8209         //    this.waitMsgTarget.unmask();
8210         //}else{
8211         //    Roo.MessageBox.updateProgress(1);
8212         //    Roo.MessageBox.hide();
8213        // }
8214         //
8215         if(success){
8216             if(o.reset){
8217                 this.reset();
8218             }
8219             Roo.callback(o.success, o.scope, [this, action]);
8220             this.fireEvent('actioncomplete', this, action);
8221
8222         }else{
8223
8224             // failure condition..
8225             // we have a scenario where updates need confirming.
8226             // eg. if a locking scenario exists..
8227             // we look for { errors : { needs_confirm : true }} in the response.
8228             if (
8229                 (typeof(action.result) != 'undefined')  &&
8230                 (typeof(action.result.errors) != 'undefined')  &&
8231                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8232            ){
8233                 var _t = this;
8234                 Roo.log("not supported yet");
8235                  /*
8236
8237                 Roo.MessageBox.confirm(
8238                     "Change requires confirmation",
8239                     action.result.errorMsg,
8240                     function(r) {
8241                         if (r != 'yes') {
8242                             return;
8243                         }
8244                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8245                     }
8246
8247                 );
8248                 */
8249
8250
8251                 return;
8252             }
8253
8254             Roo.callback(o.failure, o.scope, [this, action]);
8255             // show an error message if no failed handler is set..
8256             if (!this.hasListener('actionfailed')) {
8257                 Roo.log("need to add dialog support");
8258                 /*
8259                 Roo.MessageBox.alert("Error",
8260                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8261                         action.result.errorMsg :
8262                         "Saving Failed, please check your entries or try again"
8263                 );
8264                 */
8265             }
8266
8267             this.fireEvent('actionfailed', this, action);
8268         }
8269
8270     },
8271     /**
8272      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8273      * @param {String} id The value to search for
8274      * @return Field
8275      */
8276     findField : function(id){
8277         var items = this.getItems();
8278         var field = items.get(id);
8279         if(!field){
8280              items.each(function(f){
8281                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8282                     field = f;
8283                     return false;
8284                 }
8285                 return true;
8286             });
8287         }
8288         return field || null;
8289     },
8290      /**
8291      * Mark fields in this form invalid in bulk.
8292      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8293      * @return {BasicForm} this
8294      */
8295     markInvalid : function(errors){
8296         if(errors instanceof Array){
8297             for(var i = 0, len = errors.length; i < len; i++){
8298                 var fieldError = errors[i];
8299                 var f = this.findField(fieldError.id);
8300                 if(f){
8301                     f.markInvalid(fieldError.msg);
8302                 }
8303             }
8304         }else{
8305             var field, id;
8306             for(id in errors){
8307                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8308                     field.markInvalid(errors[id]);
8309                 }
8310             }
8311         }
8312         //Roo.each(this.childForms || [], function (f) {
8313         //    f.markInvalid(errors);
8314         //});
8315
8316         return this;
8317     },
8318
8319     /**
8320      * Set values for fields in this form in bulk.
8321      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8322      * @return {BasicForm} this
8323      */
8324     setValues : function(values){
8325         if(values instanceof Array){ // array of objects
8326             for(var i = 0, len = values.length; i < len; i++){
8327                 var v = values[i];
8328                 var f = this.findField(v.id);
8329                 if(f){
8330                     f.setValue(v.value);
8331                     if(this.trackResetOnLoad){
8332                         f.originalValue = f.getValue();
8333                     }
8334                 }
8335             }
8336         }else{ // object hash
8337             var field, id;
8338             for(id in values){
8339                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8340
8341                     if (field.setFromData &&
8342                         field.valueField &&
8343                         field.displayField &&
8344                         // combos' with local stores can
8345                         // be queried via setValue()
8346                         // to set their value..
8347                         (field.store && !field.store.isLocal)
8348                         ) {
8349                         // it's a combo
8350                         var sd = { };
8351                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8352                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8353                         field.setFromData(sd);
8354
8355                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8356                         
8357                         field.setFromData(values);
8358                         
8359                     } else {
8360                         field.setValue(values[id]);
8361                     }
8362
8363
8364                     if(this.trackResetOnLoad){
8365                         field.originalValue = field.getValue();
8366                     }
8367                 }
8368             }
8369         }
8370
8371         //Roo.each(this.childForms || [], function (f) {
8372         //    f.setValues(values);
8373         //});
8374
8375         return this;
8376     },
8377
8378     /**
8379      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8380      * they are returned as an array.
8381      * @param {Boolean} asString
8382      * @return {Object}
8383      */
8384     getValues : function(asString){
8385         //if (this.childForms) {
8386             // copy values from the child forms
8387         //    Roo.each(this.childForms, function (f) {
8388         //        this.setValues(f.getValues());
8389         //    }, this);
8390         //}
8391
8392
8393
8394         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8395         if(asString === true){
8396             return fs;
8397         }
8398         return Roo.urlDecode(fs);
8399     },
8400
8401     /**
8402      * Returns the fields in this form as an object with key/value pairs.
8403      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8404      * @return {Object}
8405      */
8406     getFieldValues : function(with_hidden)
8407     {
8408         var items = this.getItems();
8409         var ret = {};
8410         items.each(function(f){
8411             
8412             if (!f.getName()) {
8413                 return;
8414             }
8415             
8416             var v = f.getValue();
8417             
8418             if (f.inputType =='radio') {
8419                 if (typeof(ret[f.getName()]) == 'undefined') {
8420                     ret[f.getName()] = ''; // empty..
8421                 }
8422
8423                 if (!f.el.dom.checked) {
8424                     return;
8425
8426                 }
8427                 v = f.el.dom.value;
8428
8429             }
8430             
8431             if(f.xtype == 'MoneyField'){
8432                 ret[f.currencyName] = f.getCurrency();
8433             }
8434
8435             // not sure if this supported any more..
8436             if ((typeof(v) == 'object') && f.getRawValue) {
8437                 v = f.getRawValue() ; // dates..
8438             }
8439             // combo boxes where name != hiddenName...
8440             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8441                 ret[f.name] = f.getRawValue();
8442             }
8443             ret[f.getName()] = v;
8444         });
8445
8446         return ret;
8447     },
8448
8449     /**
8450      * Clears all invalid messages in this form.
8451      * @return {BasicForm} this
8452      */
8453     clearInvalid : function(){
8454         var items = this.getItems();
8455
8456         items.each(function(f){
8457            f.clearInvalid();
8458         });
8459
8460         return this;
8461     },
8462
8463     /**
8464      * Resets this form.
8465      * @return {BasicForm} this
8466      */
8467     reset : function(){
8468         var items = this.getItems();
8469         items.each(function(f){
8470             f.reset();
8471         });
8472
8473         Roo.each(this.childForms || [], function (f) {
8474             f.reset();
8475         });
8476
8477
8478         return this;
8479     },
8480     
8481     getItems : function()
8482     {
8483         var r=new Roo.util.MixedCollection(false, function(o){
8484             return o.id || (o.id = Roo.id());
8485         });
8486         var iter = function(el) {
8487             if (el.inputEl) {
8488                 r.add(el);
8489             }
8490             if (!el.items) {
8491                 return;
8492             }
8493             Roo.each(el.items,function(e) {
8494                 iter(e);
8495             });
8496         };
8497
8498         iter(this);
8499         return r;
8500     },
8501     
8502     hideFields : function(items)
8503     {
8504         Roo.each(items, function(i){
8505             
8506             var f = this.findField(i);
8507             
8508             if(!f){
8509                 return;
8510             }
8511             
8512             f.hide();
8513             
8514         }, this);
8515     },
8516     
8517     showFields : function(items)
8518     {
8519         Roo.each(items, function(i){
8520             
8521             var f = this.findField(i);
8522             
8523             if(!f){
8524                 return;
8525             }
8526             
8527             f.show();
8528             
8529         }, this);
8530     }
8531
8532 });
8533
8534 Roo.apply(Roo.bootstrap.Form, {
8535     
8536     popover : {
8537         
8538         padding : 5,
8539         
8540         isApplied : false,
8541         
8542         isMasked : false,
8543         
8544         form : false,
8545         
8546         target : false,
8547         
8548         toolTip : false,
8549         
8550         intervalID : false,
8551         
8552         maskEl : false,
8553         
8554         apply : function()
8555         {
8556             if(this.isApplied){
8557                 return;
8558             }
8559             
8560             this.maskEl = {
8561                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8562                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8563                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8564                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8565             };
8566             
8567             this.maskEl.top.enableDisplayMode("block");
8568             this.maskEl.left.enableDisplayMode("block");
8569             this.maskEl.bottom.enableDisplayMode("block");
8570             this.maskEl.right.enableDisplayMode("block");
8571             
8572             this.toolTip = new Roo.bootstrap.Tooltip({
8573                 cls : 'roo-form-error-popover',
8574                 alignment : {
8575                     'left' : ['r-l', [-2,0], 'right'],
8576                     'right' : ['l-r', [2,0], 'left'],
8577                     'bottom' : ['tl-bl', [0,2], 'top'],
8578                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8579                 }
8580             });
8581             
8582             this.toolTip.render(Roo.get(document.body));
8583
8584             this.toolTip.el.enableDisplayMode("block");
8585             
8586             Roo.get(document.body).on('click', function(){
8587                 this.unmask();
8588             }, this);
8589             
8590             Roo.get(document.body).on('touchstart', function(){
8591                 this.unmask();
8592             }, this);
8593             
8594             this.isApplied = true
8595         },
8596         
8597         mask : function(form, target)
8598         {
8599             this.form = form;
8600             
8601             this.target = target;
8602             
8603             if(!this.form.errorMask || !target.el){
8604                 return;
8605             }
8606             
8607             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8608             
8609             Roo.log(scrollable);
8610             
8611             var ot = this.target.el.calcOffsetsTo(scrollable);
8612             
8613             var scrollTo = ot[1] - this.form.maskOffset;
8614             
8615             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8616             
8617             scrollable.scrollTo('top', scrollTo);
8618             
8619             var box = this.target.el.getBox();
8620             Roo.log(box);
8621             var zIndex = Roo.bootstrap.Modal.zIndex++;
8622
8623             
8624             this.maskEl.top.setStyle('position', 'absolute');
8625             this.maskEl.top.setStyle('z-index', zIndex);
8626             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8627             this.maskEl.top.setLeft(0);
8628             this.maskEl.top.setTop(0);
8629             this.maskEl.top.show();
8630             
8631             this.maskEl.left.setStyle('position', 'absolute');
8632             this.maskEl.left.setStyle('z-index', zIndex);
8633             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8634             this.maskEl.left.setLeft(0);
8635             this.maskEl.left.setTop(box.y - this.padding);
8636             this.maskEl.left.show();
8637
8638             this.maskEl.bottom.setStyle('position', 'absolute');
8639             this.maskEl.bottom.setStyle('z-index', zIndex);
8640             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8641             this.maskEl.bottom.setLeft(0);
8642             this.maskEl.bottom.setTop(box.bottom + this.padding);
8643             this.maskEl.bottom.show();
8644
8645             this.maskEl.right.setStyle('position', 'absolute');
8646             this.maskEl.right.setStyle('z-index', zIndex);
8647             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8648             this.maskEl.right.setLeft(box.right + this.padding);
8649             this.maskEl.right.setTop(box.y - this.padding);
8650             this.maskEl.right.show();
8651
8652             this.toolTip.bindEl = this.target.el;
8653
8654             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8655
8656             var tip = this.target.blankText;
8657
8658             if(this.target.getValue() !== '' ) {
8659                 
8660                 if (this.target.invalidText.length) {
8661                     tip = this.target.invalidText;
8662                 } else if (this.target.regexText.length){
8663                     tip = this.target.regexText;
8664                 }
8665             }
8666
8667             this.toolTip.show(tip);
8668
8669             this.intervalID = window.setInterval(function() {
8670                 Roo.bootstrap.Form.popover.unmask();
8671             }, 10000);
8672
8673             window.onwheel = function(){ return false;};
8674             
8675             (function(){ this.isMasked = true; }).defer(500, this);
8676             
8677         },
8678         
8679         unmask : function()
8680         {
8681             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8682                 return;
8683             }
8684             
8685             this.maskEl.top.setStyle('position', 'absolute');
8686             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8687             this.maskEl.top.hide();
8688
8689             this.maskEl.left.setStyle('position', 'absolute');
8690             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8691             this.maskEl.left.hide();
8692
8693             this.maskEl.bottom.setStyle('position', 'absolute');
8694             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8695             this.maskEl.bottom.hide();
8696
8697             this.maskEl.right.setStyle('position', 'absolute');
8698             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8699             this.maskEl.right.hide();
8700             
8701             this.toolTip.hide();
8702             
8703             this.toolTip.el.hide();
8704             
8705             window.onwheel = function(){ return true;};
8706             
8707             if(this.intervalID){
8708                 window.clearInterval(this.intervalID);
8709                 this.intervalID = false;
8710             }
8711             
8712             this.isMasked = false;
8713             
8714         }
8715         
8716     }
8717     
8718 });
8719
8720 /*
8721  * Based on:
8722  * Ext JS Library 1.1.1
8723  * Copyright(c) 2006-2007, Ext JS, LLC.
8724  *
8725  * Originally Released Under LGPL - original licence link has changed is not relivant.
8726  *
8727  * Fork - LGPL
8728  * <script type="text/javascript">
8729  */
8730 /**
8731  * @class Roo.form.VTypes
8732  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8733  * @singleton
8734  */
8735 Roo.form.VTypes = function(){
8736     // closure these in so they are only created once.
8737     var alpha = /^[a-zA-Z_]+$/;
8738     var alphanum = /^[a-zA-Z0-9_]+$/;
8739     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8740     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8741
8742     // All these messages and functions are configurable
8743     return {
8744         /**
8745          * The function used to validate email addresses
8746          * @param {String} value The email address
8747          */
8748         'email' : function(v){
8749             return email.test(v);
8750         },
8751         /**
8752          * The error text to display when the email validation function returns false
8753          * @type String
8754          */
8755         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8756         /**
8757          * The keystroke filter mask to be applied on email input
8758          * @type RegExp
8759          */
8760         'emailMask' : /[a-z0-9_\.\-@]/i,
8761
8762         /**
8763          * The function used to validate URLs
8764          * @param {String} value The URL
8765          */
8766         'url' : function(v){
8767             return url.test(v);
8768         },
8769         /**
8770          * The error text to display when the url validation function returns false
8771          * @type String
8772          */
8773         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8774         
8775         /**
8776          * The function used to validate alpha values
8777          * @param {String} value The value
8778          */
8779         'alpha' : function(v){
8780             return alpha.test(v);
8781         },
8782         /**
8783          * The error text to display when the alpha validation function returns false
8784          * @type String
8785          */
8786         'alphaText' : 'This field should only contain letters and _',
8787         /**
8788          * The keystroke filter mask to be applied on alpha input
8789          * @type RegExp
8790          */
8791         'alphaMask' : /[a-z_]/i,
8792
8793         /**
8794          * The function used to validate alphanumeric values
8795          * @param {String} value The value
8796          */
8797         'alphanum' : function(v){
8798             return alphanum.test(v);
8799         },
8800         /**
8801          * The error text to display when the alphanumeric validation function returns false
8802          * @type String
8803          */
8804         'alphanumText' : 'This field should only contain letters, numbers and _',
8805         /**
8806          * The keystroke filter mask to be applied on alphanumeric input
8807          * @type RegExp
8808          */
8809         'alphanumMask' : /[a-z0-9_]/i
8810     };
8811 }();/*
8812  * - LGPL
8813  *
8814  * Input
8815  * 
8816  */
8817
8818 /**
8819  * @class Roo.bootstrap.Input
8820  * @extends Roo.bootstrap.Component
8821  * Bootstrap Input class
8822  * @cfg {Boolean} disabled is it disabled
8823  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8824  * @cfg {String} name name of the input
8825  * @cfg {string} fieldLabel - the label associated
8826  * @cfg {string} placeholder - placeholder to put in text.
8827  * @cfg {string}  before - input group add on before
8828  * @cfg {string} after - input group add on after
8829  * @cfg {string} size - (lg|sm) or leave empty..
8830  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8831  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8832  * @cfg {Number} md colspan out of 12 for computer-sized screens
8833  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8834  * @cfg {string} value default value of the input
8835  * @cfg {Number} labelWidth set the width of label 
8836  * @cfg {Number} labellg set the width of label (1-12)
8837  * @cfg {Number} labelmd set the width of label (1-12)
8838  * @cfg {Number} labelsm set the width of label (1-12)
8839  * @cfg {Number} labelxs set the width of label (1-12)
8840  * @cfg {String} labelAlign (top|left)
8841  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8842  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8843  * @cfg {String} indicatorpos (left|right) default left
8844  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8845  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8846
8847  * @cfg {String} align (left|center|right) Default left
8848  * @cfg {Boolean} forceFeedback (true|false) Default false
8849  * 
8850  * @constructor
8851  * Create a new Input
8852  * @param {Object} config The config object
8853  */
8854
8855 Roo.bootstrap.Input = function(config){
8856     
8857     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8858     
8859     this.addEvents({
8860         /**
8861          * @event focus
8862          * Fires when this field receives input focus.
8863          * @param {Roo.form.Field} this
8864          */
8865         focus : true,
8866         /**
8867          * @event blur
8868          * Fires when this field loses input focus.
8869          * @param {Roo.form.Field} this
8870          */
8871         blur : true,
8872         /**
8873          * @event specialkey
8874          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8875          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8876          * @param {Roo.form.Field} this
8877          * @param {Roo.EventObject} e The event object
8878          */
8879         specialkey : true,
8880         /**
8881          * @event change
8882          * Fires just before the field blurs if the field value has changed.
8883          * @param {Roo.form.Field} this
8884          * @param {Mixed} newValue The new value
8885          * @param {Mixed} oldValue The original value
8886          */
8887         change : true,
8888         /**
8889          * @event invalid
8890          * Fires after the field has been marked as invalid.
8891          * @param {Roo.form.Field} this
8892          * @param {String} msg The validation message
8893          */
8894         invalid : true,
8895         /**
8896          * @event valid
8897          * Fires after the field has been validated with no errors.
8898          * @param {Roo.form.Field} this
8899          */
8900         valid : true,
8901          /**
8902          * @event keyup
8903          * Fires after the key up
8904          * @param {Roo.form.Field} this
8905          * @param {Roo.EventObject}  e The event Object
8906          */
8907         keyup : true
8908     });
8909 };
8910
8911 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8912      /**
8913      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8914       automatic validation (defaults to "keyup").
8915      */
8916     validationEvent : "keyup",
8917      /**
8918      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8919      */
8920     validateOnBlur : true,
8921     /**
8922      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8923      */
8924     validationDelay : 250,
8925      /**
8926      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8927      */
8928     focusClass : "x-form-focus",  // not needed???
8929     
8930        
8931     /**
8932      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8933      */
8934     invalidClass : "has-warning",
8935     
8936     /**
8937      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8938      */
8939     validClass : "has-success",
8940     
8941     /**
8942      * @cfg {Boolean} hasFeedback (true|false) default true
8943      */
8944     hasFeedback : true,
8945     
8946     /**
8947      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8948      */
8949     invalidFeedbackClass : "glyphicon-warning-sign",
8950     
8951     /**
8952      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8953      */
8954     validFeedbackClass : "glyphicon-ok",
8955     
8956     /**
8957      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8958      */
8959     selectOnFocus : false,
8960     
8961      /**
8962      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8963      */
8964     maskRe : null,
8965        /**
8966      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8967      */
8968     vtype : null,
8969     
8970       /**
8971      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8972      */
8973     disableKeyFilter : false,
8974     
8975        /**
8976      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8977      */
8978     disabled : false,
8979      /**
8980      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8981      */
8982     allowBlank : true,
8983     /**
8984      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8985      */
8986     blankText : "Please complete this mandatory field",
8987     
8988      /**
8989      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8990      */
8991     minLength : 0,
8992     /**
8993      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8994      */
8995     maxLength : Number.MAX_VALUE,
8996     /**
8997      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8998      */
8999     minLengthText : "The minimum length for this field is {0}",
9000     /**
9001      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9002      */
9003     maxLengthText : "The maximum length for this field is {0}",
9004   
9005     
9006     /**
9007      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9008      * If available, this function will be called only after the basic validators all return true, and will be passed the
9009      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9010      */
9011     validator : null,
9012     /**
9013      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9014      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9015      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9016      */
9017     regex : null,
9018     /**
9019      * @cfg {String} regexText -- Depricated - use Invalid Text
9020      */
9021     regexText : "",
9022     
9023     /**
9024      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9025      */
9026     invalidText : "",
9027     
9028     
9029     
9030     autocomplete: false,
9031     
9032     
9033     fieldLabel : '',
9034     inputType : 'text',
9035     
9036     name : false,
9037     placeholder: false,
9038     before : false,
9039     after : false,
9040     size : false,
9041     hasFocus : false,
9042     preventMark: false,
9043     isFormField : true,
9044     value : '',
9045     labelWidth : 2,
9046     labelAlign : false,
9047     readOnly : false,
9048     align : false,
9049     formatedValue : false,
9050     forceFeedback : false,
9051     
9052     indicatorpos : 'left',
9053     
9054     labellg : 0,
9055     labelmd : 0,
9056     labelsm : 0,
9057     labelxs : 0,
9058     
9059     capture : '',
9060     accept : '',
9061     
9062     parentLabelAlign : function()
9063     {
9064         var parent = this;
9065         while (parent.parent()) {
9066             parent = parent.parent();
9067             if (typeof(parent.labelAlign) !='undefined') {
9068                 return parent.labelAlign;
9069             }
9070         }
9071         return 'left';
9072         
9073     },
9074     
9075     getAutoCreate : function()
9076     {
9077         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9078         
9079         var id = Roo.id();
9080         
9081         var cfg = {};
9082         
9083         if(this.inputType != 'hidden'){
9084             cfg.cls = 'form-group' //input-group
9085         }
9086         
9087         var input =  {
9088             tag: 'input',
9089             id : id,
9090             type : this.inputType,
9091             value : this.value,
9092             cls : 'form-control',
9093             placeholder : this.placeholder || '',
9094             autocomplete : this.autocomplete || 'new-password'
9095         };
9096         
9097         if(this.capture.length){
9098             input.capture = this.capture;
9099         }
9100         
9101         if(this.accept.length){
9102             input.accept = this.accept + "/*";
9103         }
9104         
9105         if(this.align){
9106             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9107         }
9108         
9109         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9110             input.maxLength = this.maxLength;
9111         }
9112         
9113         if (this.disabled) {
9114             input.disabled=true;
9115         }
9116         
9117         if (this.readOnly) {
9118             input.readonly=true;
9119         }
9120         
9121         if (this.name) {
9122             input.name = this.name;
9123         }
9124         
9125         if (this.size) {
9126             input.cls += ' input-' + this.size;
9127         }
9128         
9129         var settings=this;
9130         ['xs','sm','md','lg'].map(function(size){
9131             if (settings[size]) {
9132                 cfg.cls += ' col-' + size + '-' + settings[size];
9133             }
9134         });
9135         
9136         var inputblock = input;
9137         
9138         var feedback = {
9139             tag: 'span',
9140             cls: 'glyphicon form-control-feedback'
9141         };
9142             
9143         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9144             
9145             inputblock = {
9146                 cls : 'has-feedback',
9147                 cn :  [
9148                     input,
9149                     feedback
9150                 ] 
9151             };  
9152         }
9153         
9154         if (this.before || this.after) {
9155             
9156             inputblock = {
9157                 cls : 'input-group',
9158                 cn :  [] 
9159             };
9160             
9161             if (this.before && typeof(this.before) == 'string') {
9162                 
9163                 inputblock.cn.push({
9164                     tag :'span',
9165                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9166                     html : this.before
9167                 });
9168             }
9169             if (this.before && typeof(this.before) == 'object') {
9170                 this.before = Roo.factory(this.before);
9171                 
9172                 inputblock.cn.push({
9173                     tag :'span',
9174                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9175                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9176                 });
9177             }
9178             
9179             inputblock.cn.push(input);
9180             
9181             if (this.after && typeof(this.after) == 'string') {
9182                 inputblock.cn.push({
9183                     tag :'span',
9184                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9185                     html : this.after
9186                 });
9187             }
9188             if (this.after && typeof(this.after) == 'object') {
9189                 this.after = Roo.factory(this.after);
9190                 
9191                 inputblock.cn.push({
9192                     tag :'span',
9193                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9194                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9195                 });
9196             }
9197             
9198             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9199                 inputblock.cls += ' has-feedback';
9200                 inputblock.cn.push(feedback);
9201             }
9202         };
9203         var indicator = {
9204             tag : 'i',
9205             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9206             tooltip : 'This field is required'
9207         };
9208         if (Roo.bootstrap.version == 4) {
9209             indicator = {
9210                 tag : 'i',
9211                 style : 'display-none'
9212             };
9213         }
9214         if (align ==='left' && this.fieldLabel.length) {
9215             
9216             cfg.cls += ' roo-form-group-label-left row';
9217             
9218             cfg.cn = [
9219                 indicator,
9220                 {
9221                     tag: 'label',
9222                     'for' :  id,
9223                     cls : 'control-label col-form-label',
9224                     html : this.fieldLabel
9225
9226                 },
9227                 {
9228                     cls : "", 
9229                     cn: [
9230                         inputblock
9231                     ]
9232                 }
9233             ];
9234             
9235             var labelCfg = cfg.cn[1];
9236             var contentCfg = cfg.cn[2];
9237             
9238             if(this.indicatorpos == 'right'){
9239                 cfg.cn = [
9240                     {
9241                         tag: 'label',
9242                         'for' :  id,
9243                         cls : 'control-label col-form-label',
9244                         cn : [
9245                             {
9246                                 tag : 'span',
9247                                 html : this.fieldLabel
9248                             },
9249                             indicator
9250                         ]
9251                     },
9252                     {
9253                         cls : "",
9254                         cn: [
9255                             inputblock
9256                         ]
9257                     }
9258
9259                 ];
9260                 
9261                 labelCfg = cfg.cn[0];
9262                 contentCfg = cfg.cn[1];
9263             
9264             }
9265             
9266             if(this.labelWidth > 12){
9267                 labelCfg.style = "width: " + this.labelWidth + 'px';
9268             }
9269             
9270             if(this.labelWidth < 13 && this.labelmd == 0){
9271                 this.labelmd = this.labelWidth;
9272             }
9273             
9274             if(this.labellg > 0){
9275                 labelCfg.cls += ' col-lg-' + this.labellg;
9276                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9277             }
9278             
9279             if(this.labelmd > 0){
9280                 labelCfg.cls += ' col-md-' + this.labelmd;
9281                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9282             }
9283             
9284             if(this.labelsm > 0){
9285                 labelCfg.cls += ' col-sm-' + this.labelsm;
9286                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9287             }
9288             
9289             if(this.labelxs > 0){
9290                 labelCfg.cls += ' col-xs-' + this.labelxs;
9291                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9292             }
9293             
9294             
9295         } else if ( this.fieldLabel.length) {
9296                 
9297             cfg.cn = [
9298                 {
9299                     tag : 'i',
9300                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9301                     tooltip : 'This field is required'
9302                 },
9303                 {
9304                     tag: 'label',
9305                    //cls : 'input-group-addon',
9306                     html : this.fieldLabel
9307
9308                 },
9309
9310                inputblock
9311
9312            ];
9313            
9314            if(this.indicatorpos == 'right'){
9315                 
9316                 cfg.cn = [
9317                     {
9318                         tag: 'label',
9319                        //cls : 'input-group-addon',
9320                         html : this.fieldLabel
9321
9322                     },
9323                     {
9324                         tag : 'i',
9325                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9326                         tooltip : 'This field is required'
9327                     },
9328
9329                    inputblock
9330
9331                ];
9332
9333             }
9334
9335         } else {
9336             
9337             cfg.cn = [
9338
9339                     inputblock
9340
9341             ];
9342                 
9343                 
9344         };
9345         
9346         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9347            cfg.cls += ' navbar-form';
9348         }
9349         
9350         if (this.parentType === 'NavGroup') {
9351            cfg.cls += ' navbar-form';
9352            cfg.tag = 'li';
9353         }
9354         
9355         return cfg;
9356         
9357     },
9358     /**
9359      * return the real input element.
9360      */
9361     inputEl: function ()
9362     {
9363         return this.el.select('input.form-control',true).first();
9364     },
9365     
9366     tooltipEl : function()
9367     {
9368         return this.inputEl();
9369     },
9370     
9371     indicatorEl : function()
9372     {
9373         if (Roo.bootstrap.version == 4) {
9374             return false; // not enabled in v4 yet.
9375         }
9376         
9377         var indicator = this.el.select('i.roo-required-indicator',true).first();
9378         
9379         if(!indicator){
9380             return false;
9381         }
9382         
9383         return indicator;
9384         
9385     },
9386     
9387     setDisabled : function(v)
9388     {
9389         var i  = this.inputEl().dom;
9390         if (!v) {
9391             i.removeAttribute('disabled');
9392             return;
9393             
9394         }
9395         i.setAttribute('disabled','true');
9396     },
9397     initEvents : function()
9398     {
9399           
9400         this.inputEl().on("keydown" , this.fireKey,  this);
9401         this.inputEl().on("focus", this.onFocus,  this);
9402         this.inputEl().on("blur", this.onBlur,  this);
9403         
9404         this.inputEl().relayEvent('keyup', this);
9405         
9406         this.indicator = this.indicatorEl();
9407         
9408         if(this.indicator){
9409             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9410         }
9411  
9412         // reference to original value for reset
9413         this.originalValue = this.getValue();
9414         //Roo.form.TextField.superclass.initEvents.call(this);
9415         if(this.validationEvent == 'keyup'){
9416             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9417             this.inputEl().on('keyup', this.filterValidation, this);
9418         }
9419         else if(this.validationEvent !== false){
9420             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9421         }
9422         
9423         if(this.selectOnFocus){
9424             this.on("focus", this.preFocus, this);
9425             
9426         }
9427         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9428             this.inputEl().on("keypress", this.filterKeys, this);
9429         } else {
9430             this.inputEl().relayEvent('keypress', this);
9431         }
9432        /* if(this.grow){
9433             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9434             this.el.on("click", this.autoSize,  this);
9435         }
9436         */
9437         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9438             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9439         }
9440         
9441         if (typeof(this.before) == 'object') {
9442             this.before.render(this.el.select('.roo-input-before',true).first());
9443         }
9444         if (typeof(this.after) == 'object') {
9445             this.after.render(this.el.select('.roo-input-after',true).first());
9446         }
9447         
9448         this.inputEl().on('change', this.onChange, this);
9449         
9450     },
9451     filterValidation : function(e){
9452         if(!e.isNavKeyPress()){
9453             this.validationTask.delay(this.validationDelay);
9454         }
9455     },
9456      /**
9457      * Validates the field value
9458      * @return {Boolean} True if the value is valid, else false
9459      */
9460     validate : function(){
9461         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9462         if(this.disabled || this.validateValue(this.getRawValue())){
9463             this.markValid();
9464             return true;
9465         }
9466         
9467         this.markInvalid();
9468         return false;
9469     },
9470     
9471     
9472     /**
9473      * Validates a value according to the field's validation rules and marks the field as invalid
9474      * if the validation fails
9475      * @param {Mixed} value The value to validate
9476      * @return {Boolean} True if the value is valid, else false
9477      */
9478     validateValue : function(value)
9479     {
9480         if(this.getVisibilityEl().hasClass('hidden')){
9481             return true;
9482         }
9483         
9484         if(value.length < 1)  { // if it's blank
9485             if(this.allowBlank){
9486                 return true;
9487             }
9488             return false;
9489         }
9490         
9491         if(value.length < this.minLength){
9492             return false;
9493         }
9494         if(value.length > this.maxLength){
9495             return false;
9496         }
9497         if(this.vtype){
9498             var vt = Roo.form.VTypes;
9499             if(!vt[this.vtype](value, this)){
9500                 return false;
9501             }
9502         }
9503         if(typeof this.validator == "function"){
9504             var msg = this.validator(value);
9505             if(msg !== true){
9506                 return false;
9507             }
9508             if (typeof(msg) == 'string') {
9509                 this.invalidText = msg;
9510             }
9511         }
9512         
9513         if(this.regex && !this.regex.test(value)){
9514             return false;
9515         }
9516         
9517         return true;
9518     },
9519     
9520      // private
9521     fireKey : function(e){
9522         //Roo.log('field ' + e.getKey());
9523         if(e.isNavKeyPress()){
9524             this.fireEvent("specialkey", this, e);
9525         }
9526     },
9527     focus : function (selectText){
9528         if(this.rendered){
9529             this.inputEl().focus();
9530             if(selectText === true){
9531                 this.inputEl().dom.select();
9532             }
9533         }
9534         return this;
9535     } ,
9536     
9537     onFocus : function(){
9538         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9539            // this.el.addClass(this.focusClass);
9540         }
9541         if(!this.hasFocus){
9542             this.hasFocus = true;
9543             this.startValue = this.getValue();
9544             this.fireEvent("focus", this);
9545         }
9546     },
9547     
9548     beforeBlur : Roo.emptyFn,
9549
9550     
9551     // private
9552     onBlur : function(){
9553         this.beforeBlur();
9554         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9555             //this.el.removeClass(this.focusClass);
9556         }
9557         this.hasFocus = false;
9558         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9559             this.validate();
9560         }
9561         var v = this.getValue();
9562         if(String(v) !== String(this.startValue)){
9563             this.fireEvent('change', this, v, this.startValue);
9564         }
9565         this.fireEvent("blur", this);
9566     },
9567     
9568     onChange : function(e)
9569     {
9570         var v = this.getValue();
9571         if(String(v) !== String(this.startValue)){
9572             this.fireEvent('change', this, v, this.startValue);
9573         }
9574         
9575     },
9576     
9577     /**
9578      * Resets the current field value to the originally loaded value and clears any validation messages
9579      */
9580     reset : function(){
9581         this.setValue(this.originalValue);
9582         this.validate();
9583     },
9584      /**
9585      * Returns the name of the field
9586      * @return {Mixed} name The name field
9587      */
9588     getName: function(){
9589         return this.name;
9590     },
9591      /**
9592      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9593      * @return {Mixed} value The field value
9594      */
9595     getValue : function(){
9596         
9597         var v = this.inputEl().getValue();
9598         
9599         return v;
9600     },
9601     /**
9602      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9603      * @return {Mixed} value The field value
9604      */
9605     getRawValue : function(){
9606         var v = this.inputEl().getValue();
9607         
9608         return v;
9609     },
9610     
9611     /**
9612      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9613      * @param {Mixed} value The value to set
9614      */
9615     setRawValue : function(v){
9616         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9617     },
9618     
9619     selectText : function(start, end){
9620         var v = this.getRawValue();
9621         if(v.length > 0){
9622             start = start === undefined ? 0 : start;
9623             end = end === undefined ? v.length : end;
9624             var d = this.inputEl().dom;
9625             if(d.setSelectionRange){
9626                 d.setSelectionRange(start, end);
9627             }else if(d.createTextRange){
9628                 var range = d.createTextRange();
9629                 range.moveStart("character", start);
9630                 range.moveEnd("character", v.length-end);
9631                 range.select();
9632             }
9633         }
9634     },
9635     
9636     /**
9637      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9638      * @param {Mixed} value The value to set
9639      */
9640     setValue : function(v){
9641         this.value = v;
9642         if(this.rendered){
9643             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9644             this.validate();
9645         }
9646     },
9647     
9648     /*
9649     processValue : function(value){
9650         if(this.stripCharsRe){
9651             var newValue = value.replace(this.stripCharsRe, '');
9652             if(newValue !== value){
9653                 this.setRawValue(newValue);
9654                 return newValue;
9655             }
9656         }
9657         return value;
9658     },
9659   */
9660     preFocus : function(){
9661         
9662         if(this.selectOnFocus){
9663             this.inputEl().dom.select();
9664         }
9665     },
9666     filterKeys : function(e){
9667         var k = e.getKey();
9668         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9669             return;
9670         }
9671         var c = e.getCharCode(), cc = String.fromCharCode(c);
9672         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9673             return;
9674         }
9675         if(!this.maskRe.test(cc)){
9676             e.stopEvent();
9677         }
9678     },
9679      /**
9680      * Clear any invalid styles/messages for this field
9681      */
9682     clearInvalid : function(){
9683         
9684         if(!this.el || this.preventMark){ // not rendered
9685             return;
9686         }
9687         
9688      
9689         this.el.removeClass(this.invalidClass);
9690         
9691         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9692             
9693             var feedback = this.el.select('.form-control-feedback', true).first();
9694             
9695             if(feedback){
9696                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9697             }
9698             
9699         }
9700         
9701         if(this.indicator){
9702             this.indicator.removeClass('visible');
9703             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9704         }
9705         
9706         this.fireEvent('valid', this);
9707     },
9708     
9709      /**
9710      * Mark this field as valid
9711      */
9712     markValid : function()
9713     {
9714         if(!this.el  || this.preventMark){ // not rendered...
9715             return;
9716         }
9717         
9718         this.el.removeClass([this.invalidClass, this.validClass]);
9719         
9720         var feedback = this.el.select('.form-control-feedback', true).first();
9721             
9722         if(feedback){
9723             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9724         }
9725         
9726         if(this.indicator){
9727             this.indicator.removeClass('visible');
9728             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9729         }
9730         
9731         if(this.disabled){
9732             return;
9733         }
9734         
9735         if(this.allowBlank && !this.getRawValue().length){
9736             return;
9737         }
9738         
9739         this.el.addClass(this.validClass);
9740         
9741         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9742             
9743             var feedback = this.el.select('.form-control-feedback', true).first();
9744             
9745             if(feedback){
9746                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9747                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9748             }
9749             
9750         }
9751         
9752         this.fireEvent('valid', this);
9753     },
9754     
9755      /**
9756      * Mark this field as invalid
9757      * @param {String} msg The validation message
9758      */
9759     markInvalid : function(msg)
9760     {
9761         if(!this.el  || this.preventMark){ // not rendered
9762             return;
9763         }
9764         
9765         this.el.removeClass([this.invalidClass, this.validClass]);
9766         
9767         var feedback = this.el.select('.form-control-feedback', true).first();
9768             
9769         if(feedback){
9770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9771         }
9772
9773         if(this.disabled){
9774             return;
9775         }
9776         
9777         if(this.allowBlank && !this.getRawValue().length){
9778             return;
9779         }
9780         
9781         if(this.indicator){
9782             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9783             this.indicator.addClass('visible');
9784         }
9785         
9786         this.el.addClass(this.invalidClass);
9787         
9788         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9789             
9790             var feedback = this.el.select('.form-control-feedback', true).first();
9791             
9792             if(feedback){
9793                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9794                 
9795                 if(this.getValue().length || this.forceFeedback){
9796                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9797                 }
9798                 
9799             }
9800             
9801         }
9802         
9803         this.fireEvent('invalid', this, msg);
9804     },
9805     // private
9806     SafariOnKeyDown : function(event)
9807     {
9808         // this is a workaround for a password hang bug on chrome/ webkit.
9809         if (this.inputEl().dom.type != 'password') {
9810             return;
9811         }
9812         
9813         var isSelectAll = false;
9814         
9815         if(this.inputEl().dom.selectionEnd > 0){
9816             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9817         }
9818         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9819             event.preventDefault();
9820             this.setValue('');
9821             return;
9822         }
9823         
9824         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9825             
9826             event.preventDefault();
9827             // this is very hacky as keydown always get's upper case.
9828             //
9829             var cc = String.fromCharCode(event.getCharCode());
9830             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9831             
9832         }
9833     },
9834     adjustWidth : function(tag, w){
9835         tag = tag.toLowerCase();
9836         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9837             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9838                 if(tag == 'input'){
9839                     return w + 2;
9840                 }
9841                 if(tag == 'textarea'){
9842                     return w-2;
9843                 }
9844             }else if(Roo.isOpera){
9845                 if(tag == 'input'){
9846                     return w + 2;
9847                 }
9848                 if(tag == 'textarea'){
9849                     return w-2;
9850                 }
9851             }
9852         }
9853         return w;
9854     },
9855     
9856     setFieldLabel : function(v)
9857     {
9858         if(!this.rendered){
9859             return;
9860         }
9861         
9862         if(this.indicatorEl()){
9863             var ar = this.el.select('label > span',true);
9864             
9865             if (ar.elements.length) {
9866                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9867                 this.fieldLabel = v;
9868                 return;
9869             }
9870             
9871             var br = this.el.select('label',true);
9872             
9873             if(br.elements.length) {
9874                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9875                 this.fieldLabel = v;
9876                 return;
9877             }
9878             
9879             Roo.log('Cannot Found any of label > span || label in input');
9880             return;
9881         }
9882         
9883         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9884         this.fieldLabel = v;
9885         
9886         
9887     }
9888 });
9889
9890  
9891 /*
9892  * - LGPL
9893  *
9894  * Input
9895  * 
9896  */
9897
9898 /**
9899  * @class Roo.bootstrap.TextArea
9900  * @extends Roo.bootstrap.Input
9901  * Bootstrap TextArea class
9902  * @cfg {Number} cols Specifies the visible width of a text area
9903  * @cfg {Number} rows Specifies the visible number of lines in a text area
9904  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9905  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9906  * @cfg {string} html text
9907  * 
9908  * @constructor
9909  * Create a new TextArea
9910  * @param {Object} config The config object
9911  */
9912
9913 Roo.bootstrap.TextArea = function(config){
9914     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9915    
9916 };
9917
9918 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9919      
9920     cols : false,
9921     rows : 5,
9922     readOnly : false,
9923     warp : 'soft',
9924     resize : false,
9925     value: false,
9926     html: false,
9927     
9928     getAutoCreate : function(){
9929         
9930         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9931         
9932         var id = Roo.id();
9933         
9934         var cfg = {};
9935         
9936         if(this.inputType != 'hidden'){
9937             cfg.cls = 'form-group' //input-group
9938         }
9939         
9940         var input =  {
9941             tag: 'textarea',
9942             id : id,
9943             warp : this.warp,
9944             rows : this.rows,
9945             value : this.value || '',
9946             html: this.html || '',
9947             cls : 'form-control',
9948             placeholder : this.placeholder || '' 
9949             
9950         };
9951         
9952         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9953             input.maxLength = this.maxLength;
9954         }
9955         
9956         if(this.resize){
9957             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9958         }
9959         
9960         if(this.cols){
9961             input.cols = this.cols;
9962         }
9963         
9964         if (this.readOnly) {
9965             input.readonly = true;
9966         }
9967         
9968         if (this.name) {
9969             input.name = this.name;
9970         }
9971         
9972         if (this.size) {
9973             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9974         }
9975         
9976         var settings=this;
9977         ['xs','sm','md','lg'].map(function(size){
9978             if (settings[size]) {
9979                 cfg.cls += ' col-' + size + '-' + settings[size];
9980             }
9981         });
9982         
9983         var inputblock = input;
9984         
9985         if(this.hasFeedback && !this.allowBlank){
9986             
9987             var feedback = {
9988                 tag: 'span',
9989                 cls: 'glyphicon form-control-feedback'
9990             };
9991
9992             inputblock = {
9993                 cls : 'has-feedback',
9994                 cn :  [
9995                     input,
9996                     feedback
9997                 ] 
9998             };  
9999         }
10000         
10001         
10002         if (this.before || this.after) {
10003             
10004             inputblock = {
10005                 cls : 'input-group',
10006                 cn :  [] 
10007             };
10008             if (this.before) {
10009                 inputblock.cn.push({
10010                     tag :'span',
10011                     cls : 'input-group-addon',
10012                     html : this.before
10013                 });
10014             }
10015             
10016             inputblock.cn.push(input);
10017             
10018             if(this.hasFeedback && !this.allowBlank){
10019                 inputblock.cls += ' has-feedback';
10020                 inputblock.cn.push(feedback);
10021             }
10022             
10023             if (this.after) {
10024                 inputblock.cn.push({
10025                     tag :'span',
10026                     cls : 'input-group-addon',
10027                     html : this.after
10028                 });
10029             }
10030             
10031         }
10032         
10033         if (align ==='left' && this.fieldLabel.length) {
10034             cfg.cn = [
10035                 {
10036                     tag: 'label',
10037                     'for' :  id,
10038                     cls : 'control-label',
10039                     html : this.fieldLabel
10040                 },
10041                 {
10042                     cls : "",
10043                     cn: [
10044                         inputblock
10045                     ]
10046                 }
10047
10048             ];
10049             
10050             if(this.labelWidth > 12){
10051                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10052             }
10053
10054             if(this.labelWidth < 13 && this.labelmd == 0){
10055                 this.labelmd = this.labelWidth;
10056             }
10057
10058             if(this.labellg > 0){
10059                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10060                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10061             }
10062
10063             if(this.labelmd > 0){
10064                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10065                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10066             }
10067
10068             if(this.labelsm > 0){
10069                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10070                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10071             }
10072
10073             if(this.labelxs > 0){
10074                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10075                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10076             }
10077             
10078         } else if ( this.fieldLabel.length) {
10079             cfg.cn = [
10080
10081                {
10082                    tag: 'label',
10083                    //cls : 'input-group-addon',
10084                    html : this.fieldLabel
10085
10086                },
10087
10088                inputblock
10089
10090            ];
10091
10092         } else {
10093
10094             cfg.cn = [
10095
10096                 inputblock
10097
10098             ];
10099                 
10100         }
10101         
10102         if (this.disabled) {
10103             input.disabled=true;
10104         }
10105         
10106         return cfg;
10107         
10108     },
10109     /**
10110      * return the real textarea element.
10111      */
10112     inputEl: function ()
10113     {
10114         return this.el.select('textarea.form-control',true).first();
10115     },
10116     
10117     /**
10118      * Clear any invalid styles/messages for this field
10119      */
10120     clearInvalid : function()
10121     {
10122         
10123         if(!this.el || this.preventMark){ // not rendered
10124             return;
10125         }
10126         
10127         var label = this.el.select('label', true).first();
10128         var icon = this.el.select('i.fa-star', true).first();
10129         
10130         if(label && icon){
10131             icon.remove();
10132         }
10133         
10134         this.el.removeClass(this.invalidClass);
10135         
10136         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10137             
10138             var feedback = this.el.select('.form-control-feedback', true).first();
10139             
10140             if(feedback){
10141                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10142             }
10143             
10144         }
10145         
10146         this.fireEvent('valid', this);
10147     },
10148     
10149      /**
10150      * Mark this field as valid
10151      */
10152     markValid : function()
10153     {
10154         if(!this.el  || this.preventMark){ // not rendered
10155             return;
10156         }
10157         
10158         this.el.removeClass([this.invalidClass, this.validClass]);
10159         
10160         var feedback = this.el.select('.form-control-feedback', true).first();
10161             
10162         if(feedback){
10163             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10164         }
10165
10166         if(this.disabled || this.allowBlank){
10167             return;
10168         }
10169         
10170         var label = this.el.select('label', true).first();
10171         var icon = this.el.select('i.fa-star', true).first();
10172         
10173         if(label && icon){
10174             icon.remove();
10175         }
10176         
10177         this.el.addClass(this.validClass);
10178         
10179         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10180             
10181             var feedback = this.el.select('.form-control-feedback', true).first();
10182             
10183             if(feedback){
10184                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10185                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10186             }
10187             
10188         }
10189         
10190         this.fireEvent('valid', this);
10191     },
10192     
10193      /**
10194      * Mark this field as invalid
10195      * @param {String} msg The validation message
10196      */
10197     markInvalid : function(msg)
10198     {
10199         if(!this.el  || this.preventMark){ // not rendered
10200             return;
10201         }
10202         
10203         this.el.removeClass([this.invalidClass, this.validClass]);
10204         
10205         var feedback = this.el.select('.form-control-feedback', true).first();
10206             
10207         if(feedback){
10208             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10209         }
10210
10211         if(this.disabled || this.allowBlank){
10212             return;
10213         }
10214         
10215         var label = this.el.select('label', true).first();
10216         var icon = this.el.select('i.fa-star', true).first();
10217         
10218         if(!this.getValue().length && label && !icon){
10219             this.el.createChild({
10220                 tag : 'i',
10221                 cls : 'text-danger fa fa-lg fa-star',
10222                 tooltip : 'This field is required',
10223                 style : 'margin-right:5px;'
10224             }, label, true);
10225         }
10226
10227         this.el.addClass(this.invalidClass);
10228         
10229         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10230             
10231             var feedback = this.el.select('.form-control-feedback', true).first();
10232             
10233             if(feedback){
10234                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10235                 
10236                 if(this.getValue().length || this.forceFeedback){
10237                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10238                 }
10239                 
10240             }
10241             
10242         }
10243         
10244         this.fireEvent('invalid', this, msg);
10245     }
10246 });
10247
10248  
10249 /*
10250  * - LGPL
10251  *
10252  * trigger field - base class for combo..
10253  * 
10254  */
10255  
10256 /**
10257  * @class Roo.bootstrap.TriggerField
10258  * @extends Roo.bootstrap.Input
10259  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10260  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10261  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10262  * for which you can provide a custom implementation.  For example:
10263  * <pre><code>
10264 var trigger = new Roo.bootstrap.TriggerField();
10265 trigger.onTriggerClick = myTriggerFn;
10266 trigger.applyTo('my-field');
10267 </code></pre>
10268  *
10269  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10270  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10271  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10272  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10273  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10274
10275  * @constructor
10276  * Create a new TriggerField.
10277  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10278  * to the base TextField)
10279  */
10280 Roo.bootstrap.TriggerField = function(config){
10281     this.mimicing = false;
10282     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10283 };
10284
10285 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10286     /**
10287      * @cfg {String} triggerClass A CSS class to apply to the trigger
10288      */
10289      /**
10290      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10291      */
10292     hideTrigger:false,
10293
10294     /**
10295      * @cfg {Boolean} removable (true|false) special filter default false
10296      */
10297     removable : false,
10298     
10299     /** @cfg {Boolean} grow @hide */
10300     /** @cfg {Number} growMin @hide */
10301     /** @cfg {Number} growMax @hide */
10302
10303     /**
10304      * @hide 
10305      * @method
10306      */
10307     autoSize: Roo.emptyFn,
10308     // private
10309     monitorTab : true,
10310     // private
10311     deferHeight : true,
10312
10313     
10314     actionMode : 'wrap',
10315     
10316     caret : false,
10317     
10318     
10319     getAutoCreate : function(){
10320        
10321         var align = this.labelAlign || this.parentLabelAlign();
10322         
10323         var id = Roo.id();
10324         
10325         var cfg = {
10326             cls: 'form-group' //input-group
10327         };
10328         
10329         
10330         var input =  {
10331             tag: 'input',
10332             id : id,
10333             type : this.inputType,
10334             cls : 'form-control',
10335             autocomplete: 'new-password',
10336             placeholder : this.placeholder || '' 
10337             
10338         };
10339         if (this.name) {
10340             input.name = this.name;
10341         }
10342         if (this.size) {
10343             input.cls += ' input-' + this.size;
10344         }
10345         
10346         if (this.disabled) {
10347             input.disabled=true;
10348         }
10349         
10350         var inputblock = input;
10351         
10352         if(this.hasFeedback && !this.allowBlank){
10353             
10354             var feedback = {
10355                 tag: 'span',
10356                 cls: 'glyphicon form-control-feedback'
10357             };
10358             
10359             if(this.removable && !this.editable && !this.tickable){
10360                 inputblock = {
10361                     cls : 'has-feedback',
10362                     cn :  [
10363                         inputblock,
10364                         {
10365                             tag: 'button',
10366                             html : 'x',
10367                             cls : 'roo-combo-removable-btn close'
10368                         },
10369                         feedback
10370                     ] 
10371                 };
10372             } else {
10373                 inputblock = {
10374                     cls : 'has-feedback',
10375                     cn :  [
10376                         inputblock,
10377                         feedback
10378                     ] 
10379                 };
10380             }
10381
10382         } else {
10383             if(this.removable && !this.editable && !this.tickable){
10384                 inputblock = {
10385                     cls : 'roo-removable',
10386                     cn :  [
10387                         inputblock,
10388                         {
10389                             tag: 'button',
10390                             html : 'x',
10391                             cls : 'roo-combo-removable-btn close'
10392                         }
10393                     ] 
10394                 };
10395             }
10396         }
10397         
10398         if (this.before || this.after) {
10399             
10400             inputblock = {
10401                 cls : 'input-group',
10402                 cn :  [] 
10403             };
10404             if (this.before) {
10405                 inputblock.cn.push({
10406                     tag :'span',
10407                     cls : 'input-group-addon input-group-prepend input-group-text',
10408                     html : this.before
10409                 });
10410             }
10411             
10412             inputblock.cn.push(input);
10413             
10414             if(this.hasFeedback && !this.allowBlank){
10415                 inputblock.cls += ' has-feedback';
10416                 inputblock.cn.push(feedback);
10417             }
10418             
10419             if (this.after) {
10420                 inputblock.cn.push({
10421                     tag :'span',
10422                     cls : 'input-group-addon input-group-append input-group-text',
10423                     html : this.after
10424                 });
10425             }
10426             
10427         };
10428         
10429       
10430         
10431         var ibwrap = inputblock;
10432         
10433         if(this.multiple){
10434             ibwrap = {
10435                 tag: 'ul',
10436                 cls: 'roo-select2-choices',
10437                 cn:[
10438                     {
10439                         tag: 'li',
10440                         cls: 'roo-select2-search-field',
10441                         cn: [
10442
10443                             inputblock
10444                         ]
10445                     }
10446                 ]
10447             };
10448                 
10449         }
10450         
10451         var combobox = {
10452             cls: 'roo-select2-container input-group',
10453             cn: [
10454                  {
10455                     tag: 'input',
10456                     type : 'hidden',
10457                     cls: 'form-hidden-field'
10458                 },
10459                 ibwrap
10460             ]
10461         };
10462         
10463         if(!this.multiple && this.showToggleBtn){
10464             
10465             var caret = {
10466                         tag: 'span',
10467                         cls: 'caret'
10468              };
10469             if (this.caret != false) {
10470                 caret = {
10471                      tag: 'i',
10472                      cls: 'fa fa-' + this.caret
10473                 };
10474                 
10475             }
10476             
10477             combobox.cn.push({
10478                 tag :'span',
10479                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10480                 cn : [
10481                     caret,
10482                     {
10483                         tag: 'span',
10484                         cls: 'combobox-clear',
10485                         cn  : [
10486                             {
10487                                 tag : 'i',
10488                                 cls: 'icon-remove'
10489                             }
10490                         ]
10491                     }
10492                 ]
10493
10494             })
10495         }
10496         
10497         if(this.multiple){
10498             combobox.cls += ' roo-select2-container-multi';
10499         }
10500          var indicator = {
10501             tag : 'i',
10502             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10503             tooltip : 'This field is required'
10504         };
10505         if (Roo.bootstrap.version == 4) {
10506             indicator = {
10507                 tag : 'i',
10508                 style : 'display:none'
10509             };
10510         }
10511         
10512         
10513         if (align ==='left' && this.fieldLabel.length) {
10514             
10515             cfg.cls += ' roo-form-group-label-left row';
10516
10517             cfg.cn = [
10518                 indicator,
10519                 {
10520                     tag: 'label',
10521                     'for' :  id,
10522                     cls : 'control-label',
10523                     html : this.fieldLabel
10524
10525                 },
10526                 {
10527                     cls : "", 
10528                     cn: [
10529                         combobox
10530                     ]
10531                 }
10532
10533             ];
10534             
10535             var labelCfg = cfg.cn[1];
10536             var contentCfg = cfg.cn[2];
10537             
10538             if(this.indicatorpos == 'right'){
10539                 cfg.cn = [
10540                     {
10541                         tag: 'label',
10542                         'for' :  id,
10543                         cls : 'control-label',
10544                         cn : [
10545                             {
10546                                 tag : 'span',
10547                                 html : this.fieldLabel
10548                             },
10549                             indicator
10550                         ]
10551                     },
10552                     {
10553                         cls : "", 
10554                         cn: [
10555                             combobox
10556                         ]
10557                     }
10558
10559                 ];
10560                 
10561                 labelCfg = cfg.cn[0];
10562                 contentCfg = cfg.cn[1];
10563             }
10564             
10565             if(this.labelWidth > 12){
10566                 labelCfg.style = "width: " + this.labelWidth + 'px';
10567             }
10568             
10569             if(this.labelWidth < 13 && this.labelmd == 0){
10570                 this.labelmd = this.labelWidth;
10571             }
10572             
10573             if(this.labellg > 0){
10574                 labelCfg.cls += ' col-lg-' + this.labellg;
10575                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10576             }
10577             
10578             if(this.labelmd > 0){
10579                 labelCfg.cls += ' col-md-' + this.labelmd;
10580                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10581             }
10582             
10583             if(this.labelsm > 0){
10584                 labelCfg.cls += ' col-sm-' + this.labelsm;
10585                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10586             }
10587             
10588             if(this.labelxs > 0){
10589                 labelCfg.cls += ' col-xs-' + this.labelxs;
10590                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10591             }
10592             
10593         } else if ( this.fieldLabel.length) {
10594 //                Roo.log(" label");
10595             cfg.cn = [
10596                 indicator,
10597                {
10598                    tag: 'label',
10599                    //cls : 'input-group-addon',
10600                    html : this.fieldLabel
10601
10602                },
10603
10604                combobox
10605
10606             ];
10607             
10608             if(this.indicatorpos == 'right'){
10609                 
10610                 cfg.cn = [
10611                     {
10612                        tag: 'label',
10613                        cn : [
10614                            {
10615                                tag : 'span',
10616                                html : this.fieldLabel
10617                            },
10618                            indicator
10619                        ]
10620
10621                     },
10622                     combobox
10623
10624                 ];
10625
10626             }
10627
10628         } else {
10629             
10630 //                Roo.log(" no label && no align");
10631                 cfg = combobox
10632                      
10633                 
10634         }
10635         
10636         var settings=this;
10637         ['xs','sm','md','lg'].map(function(size){
10638             if (settings[size]) {
10639                 cfg.cls += ' col-' + size + '-' + settings[size];
10640             }
10641         });
10642         
10643         return cfg;
10644         
10645     },
10646     
10647     
10648     
10649     // private
10650     onResize : function(w, h){
10651 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10652 //        if(typeof w == 'number'){
10653 //            var x = w - this.trigger.getWidth();
10654 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10655 //            this.trigger.setStyle('left', x+'px');
10656 //        }
10657     },
10658
10659     // private
10660     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10661
10662     // private
10663     getResizeEl : function(){
10664         return this.inputEl();
10665     },
10666
10667     // private
10668     getPositionEl : function(){
10669         return this.inputEl();
10670     },
10671
10672     // private
10673     alignErrorIcon : function(){
10674         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10675     },
10676
10677     // private
10678     initEvents : function(){
10679         
10680         this.createList();
10681         
10682         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10683         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10684         if(!this.multiple && this.showToggleBtn){
10685             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10686             if(this.hideTrigger){
10687                 this.trigger.setDisplayed(false);
10688             }
10689             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10690         }
10691         
10692         if(this.multiple){
10693             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10694         }
10695         
10696         if(this.removable && !this.editable && !this.tickable){
10697             var close = this.closeTriggerEl();
10698             
10699             if(close){
10700                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10701                 close.on('click', this.removeBtnClick, this, close);
10702             }
10703         }
10704         
10705         //this.trigger.addClassOnOver('x-form-trigger-over');
10706         //this.trigger.addClassOnClick('x-form-trigger-click');
10707         
10708         //if(!this.width){
10709         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10710         //}
10711     },
10712     
10713     closeTriggerEl : function()
10714     {
10715         var close = this.el.select('.roo-combo-removable-btn', true).first();
10716         return close ? close : false;
10717     },
10718     
10719     removeBtnClick : function(e, h, el)
10720     {
10721         e.preventDefault();
10722         
10723         if(this.fireEvent("remove", this) !== false){
10724             this.reset();
10725             this.fireEvent("afterremove", this)
10726         }
10727     },
10728     
10729     createList : function()
10730     {
10731         this.list = Roo.get(document.body).createChild({
10732             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10733             cls: 'typeahead typeahead-long dropdown-menu',
10734             style: 'display:none'
10735         });
10736         
10737         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10738         
10739     },
10740
10741     // private
10742     initTrigger : function(){
10743        
10744     },
10745
10746     // private
10747     onDestroy : function(){
10748         if(this.trigger){
10749             this.trigger.removeAllListeners();
10750           //  this.trigger.remove();
10751         }
10752         //if(this.wrap){
10753         //    this.wrap.remove();
10754         //}
10755         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10756     },
10757
10758     // private
10759     onFocus : function(){
10760         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10761         /*
10762         if(!this.mimicing){
10763             this.wrap.addClass('x-trigger-wrap-focus');
10764             this.mimicing = true;
10765             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10766             if(this.monitorTab){
10767                 this.el.on("keydown", this.checkTab, this);
10768             }
10769         }
10770         */
10771     },
10772
10773     // private
10774     checkTab : function(e){
10775         if(e.getKey() == e.TAB){
10776             this.triggerBlur();
10777         }
10778     },
10779
10780     // private
10781     onBlur : function(){
10782         // do nothing
10783     },
10784
10785     // private
10786     mimicBlur : function(e, t){
10787         /*
10788         if(!this.wrap.contains(t) && this.validateBlur()){
10789             this.triggerBlur();
10790         }
10791         */
10792     },
10793
10794     // private
10795     triggerBlur : function(){
10796         this.mimicing = false;
10797         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10798         if(this.monitorTab){
10799             this.el.un("keydown", this.checkTab, this);
10800         }
10801         //this.wrap.removeClass('x-trigger-wrap-focus');
10802         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10803     },
10804
10805     // private
10806     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10807     validateBlur : function(e, t){
10808         return true;
10809     },
10810
10811     // private
10812     onDisable : function(){
10813         this.inputEl().dom.disabled = true;
10814         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10815         //if(this.wrap){
10816         //    this.wrap.addClass('x-item-disabled');
10817         //}
10818     },
10819
10820     // private
10821     onEnable : function(){
10822         this.inputEl().dom.disabled = false;
10823         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10824         //if(this.wrap){
10825         //    this.el.removeClass('x-item-disabled');
10826         //}
10827     },
10828
10829     // private
10830     onShow : function(){
10831         var ae = this.getActionEl();
10832         
10833         if(ae){
10834             ae.dom.style.display = '';
10835             ae.dom.style.visibility = 'visible';
10836         }
10837     },
10838
10839     // private
10840     
10841     onHide : function(){
10842         var ae = this.getActionEl();
10843         ae.dom.style.display = 'none';
10844     },
10845
10846     /**
10847      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10848      * by an implementing function.
10849      * @method
10850      * @param {EventObject} e
10851      */
10852     onTriggerClick : Roo.emptyFn
10853 });
10854  /*
10855  * Based on:
10856  * Ext JS Library 1.1.1
10857  * Copyright(c) 2006-2007, Ext JS, LLC.
10858  *
10859  * Originally Released Under LGPL - original licence link has changed is not relivant.
10860  *
10861  * Fork - LGPL
10862  * <script type="text/javascript">
10863  */
10864
10865
10866 /**
10867  * @class Roo.data.SortTypes
10868  * @singleton
10869  * Defines the default sorting (casting?) comparison functions used when sorting data.
10870  */
10871 Roo.data.SortTypes = {
10872     /**
10873      * Default sort that does nothing
10874      * @param {Mixed} s The value being converted
10875      * @return {Mixed} The comparison value
10876      */
10877     none : function(s){
10878         return s;
10879     },
10880     
10881     /**
10882      * The regular expression used to strip tags
10883      * @type {RegExp}
10884      * @property
10885      */
10886     stripTagsRE : /<\/?[^>]+>/gi,
10887     
10888     /**
10889      * Strips all HTML tags to sort on text only
10890      * @param {Mixed} s The value being converted
10891      * @return {String} The comparison value
10892      */
10893     asText : function(s){
10894         return String(s).replace(this.stripTagsRE, "");
10895     },
10896     
10897     /**
10898      * Strips all HTML tags to sort on text only - Case insensitive
10899      * @param {Mixed} s The value being converted
10900      * @return {String} The comparison value
10901      */
10902     asUCText : function(s){
10903         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10904     },
10905     
10906     /**
10907      * Case insensitive string
10908      * @param {Mixed} s The value being converted
10909      * @return {String} The comparison value
10910      */
10911     asUCString : function(s) {
10912         return String(s).toUpperCase();
10913     },
10914     
10915     /**
10916      * Date sorting
10917      * @param {Mixed} s The value being converted
10918      * @return {Number} The comparison value
10919      */
10920     asDate : function(s) {
10921         if(!s){
10922             return 0;
10923         }
10924         if(s instanceof Date){
10925             return s.getTime();
10926         }
10927         return Date.parse(String(s));
10928     },
10929     
10930     /**
10931      * Float sorting
10932      * @param {Mixed} s The value being converted
10933      * @return {Float} The comparison value
10934      */
10935     asFloat : function(s) {
10936         var val = parseFloat(String(s).replace(/,/g, ""));
10937         if(isNaN(val)) {
10938             val = 0;
10939         }
10940         return val;
10941     },
10942     
10943     /**
10944      * Integer sorting
10945      * @param {Mixed} s The value being converted
10946      * @return {Number} The comparison value
10947      */
10948     asInt : function(s) {
10949         var val = parseInt(String(s).replace(/,/g, ""));
10950         if(isNaN(val)) {
10951             val = 0;
10952         }
10953         return val;
10954     }
10955 };/*
10956  * Based on:
10957  * Ext JS Library 1.1.1
10958  * Copyright(c) 2006-2007, Ext JS, LLC.
10959  *
10960  * Originally Released Under LGPL - original licence link has changed is not relivant.
10961  *
10962  * Fork - LGPL
10963  * <script type="text/javascript">
10964  */
10965
10966 /**
10967 * @class Roo.data.Record
10968  * Instances of this class encapsulate both record <em>definition</em> information, and record
10969  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10970  * to access Records cached in an {@link Roo.data.Store} object.<br>
10971  * <p>
10972  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10973  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10974  * objects.<br>
10975  * <p>
10976  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10977  * @constructor
10978  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10979  * {@link #create}. The parameters are the same.
10980  * @param {Array} data An associative Array of data values keyed by the field name.
10981  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10982  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10983  * not specified an integer id is generated.
10984  */
10985 Roo.data.Record = function(data, id){
10986     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10987     this.data = data;
10988 };
10989
10990 /**
10991  * Generate a constructor for a specific record layout.
10992  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10993  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10994  * Each field definition object may contain the following properties: <ul>
10995  * <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,
10996  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10997  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10998  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10999  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11000  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11001  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11002  * this may be omitted.</p></li>
11003  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11004  * <ul><li>auto (Default, implies no conversion)</li>
11005  * <li>string</li>
11006  * <li>int</li>
11007  * <li>float</li>
11008  * <li>boolean</li>
11009  * <li>date</li></ul></p></li>
11010  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11011  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11012  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11013  * by the Reader into an object that will be stored in the Record. It is passed the
11014  * following parameters:<ul>
11015  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11016  * </ul></p></li>
11017  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11018  * </ul>
11019  * <br>usage:<br><pre><code>
11020 var TopicRecord = Roo.data.Record.create(
11021     {name: 'title', mapping: 'topic_title'},
11022     {name: 'author', mapping: 'username'},
11023     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11024     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11025     {name: 'lastPoster', mapping: 'user2'},
11026     {name: 'excerpt', mapping: 'post_text'}
11027 );
11028
11029 var myNewRecord = new TopicRecord({
11030     title: 'Do my job please',
11031     author: 'noobie',
11032     totalPosts: 1,
11033     lastPost: new Date(),
11034     lastPoster: 'Animal',
11035     excerpt: 'No way dude!'
11036 });
11037 myStore.add(myNewRecord);
11038 </code></pre>
11039  * @method create
11040  * @static
11041  */
11042 Roo.data.Record.create = function(o){
11043     var f = function(){
11044         f.superclass.constructor.apply(this, arguments);
11045     };
11046     Roo.extend(f, Roo.data.Record);
11047     var p = f.prototype;
11048     p.fields = new Roo.util.MixedCollection(false, function(field){
11049         return field.name;
11050     });
11051     for(var i = 0, len = o.length; i < len; i++){
11052         p.fields.add(new Roo.data.Field(o[i]));
11053     }
11054     f.getField = function(name){
11055         return p.fields.get(name);  
11056     };
11057     return f;
11058 };
11059
11060 Roo.data.Record.AUTO_ID = 1000;
11061 Roo.data.Record.EDIT = 'edit';
11062 Roo.data.Record.REJECT = 'reject';
11063 Roo.data.Record.COMMIT = 'commit';
11064
11065 Roo.data.Record.prototype = {
11066     /**
11067      * Readonly flag - true if this record has been modified.
11068      * @type Boolean
11069      */
11070     dirty : false,
11071     editing : false,
11072     error: null,
11073     modified: null,
11074
11075     // private
11076     join : function(store){
11077         this.store = store;
11078     },
11079
11080     /**
11081      * Set the named field to the specified value.
11082      * @param {String} name The name of the field to set.
11083      * @param {Object} value The value to set the field to.
11084      */
11085     set : function(name, value){
11086         if(this.data[name] == value){
11087             return;
11088         }
11089         this.dirty = true;
11090         if(!this.modified){
11091             this.modified = {};
11092         }
11093         if(typeof this.modified[name] == 'undefined'){
11094             this.modified[name] = this.data[name];
11095         }
11096         this.data[name] = value;
11097         if(!this.editing && this.store){
11098             this.store.afterEdit(this);
11099         }       
11100     },
11101
11102     /**
11103      * Get the value of the named field.
11104      * @param {String} name The name of the field to get the value of.
11105      * @return {Object} The value of the field.
11106      */
11107     get : function(name){
11108         return this.data[name]; 
11109     },
11110
11111     // private
11112     beginEdit : function(){
11113         this.editing = true;
11114         this.modified = {}; 
11115     },
11116
11117     // private
11118     cancelEdit : function(){
11119         this.editing = false;
11120         delete this.modified;
11121     },
11122
11123     // private
11124     endEdit : function(){
11125         this.editing = false;
11126         if(this.dirty && this.store){
11127             this.store.afterEdit(this);
11128         }
11129     },
11130
11131     /**
11132      * Usually called by the {@link Roo.data.Store} which owns the Record.
11133      * Rejects all changes made to the Record since either creation, or the last commit operation.
11134      * Modified fields are reverted to their original values.
11135      * <p>
11136      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11137      * of reject operations.
11138      */
11139     reject : function(){
11140         var m = this.modified;
11141         for(var n in m){
11142             if(typeof m[n] != "function"){
11143                 this.data[n] = m[n];
11144             }
11145         }
11146         this.dirty = false;
11147         delete this.modified;
11148         this.editing = false;
11149         if(this.store){
11150             this.store.afterReject(this);
11151         }
11152     },
11153
11154     /**
11155      * Usually called by the {@link Roo.data.Store} which owns the Record.
11156      * Commits all changes made to the Record since either creation, or the last commit operation.
11157      * <p>
11158      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11159      * of commit operations.
11160      */
11161     commit : function(){
11162         this.dirty = false;
11163         delete this.modified;
11164         this.editing = false;
11165         if(this.store){
11166             this.store.afterCommit(this);
11167         }
11168     },
11169
11170     // private
11171     hasError : function(){
11172         return this.error != null;
11173     },
11174
11175     // private
11176     clearError : function(){
11177         this.error = null;
11178     },
11179
11180     /**
11181      * Creates a copy of this record.
11182      * @param {String} id (optional) A new record id if you don't want to use this record's id
11183      * @return {Record}
11184      */
11185     copy : function(newId) {
11186         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11187     }
11188 };/*
11189  * Based on:
11190  * Ext JS Library 1.1.1
11191  * Copyright(c) 2006-2007, Ext JS, LLC.
11192  *
11193  * Originally Released Under LGPL - original licence link has changed is not relivant.
11194  *
11195  * Fork - LGPL
11196  * <script type="text/javascript">
11197  */
11198
11199
11200
11201 /**
11202  * @class Roo.data.Store
11203  * @extends Roo.util.Observable
11204  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11205  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11206  * <p>
11207  * 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
11208  * has no knowledge of the format of the data returned by the Proxy.<br>
11209  * <p>
11210  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11211  * instances from the data object. These records are cached and made available through accessor functions.
11212  * @constructor
11213  * Creates a new Store.
11214  * @param {Object} config A config object containing the objects needed for the Store to access data,
11215  * and read the data into Records.
11216  */
11217 Roo.data.Store = function(config){
11218     this.data = new Roo.util.MixedCollection(false);
11219     this.data.getKey = function(o){
11220         return o.id;
11221     };
11222     this.baseParams = {};
11223     // private
11224     this.paramNames = {
11225         "start" : "start",
11226         "limit" : "limit",
11227         "sort" : "sort",
11228         "dir" : "dir",
11229         "multisort" : "_multisort"
11230     };
11231
11232     if(config && config.data){
11233         this.inlineData = config.data;
11234         delete config.data;
11235     }
11236
11237     Roo.apply(this, config);
11238     
11239     if(this.reader){ // reader passed
11240         this.reader = Roo.factory(this.reader, Roo.data);
11241         this.reader.xmodule = this.xmodule || false;
11242         if(!this.recordType){
11243             this.recordType = this.reader.recordType;
11244         }
11245         if(this.reader.onMetaChange){
11246             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11247         }
11248     }
11249
11250     if(this.recordType){
11251         this.fields = this.recordType.prototype.fields;
11252     }
11253     this.modified = [];
11254
11255     this.addEvents({
11256         /**
11257          * @event datachanged
11258          * Fires when the data cache has changed, and a widget which is using this Store
11259          * as a Record cache should refresh its view.
11260          * @param {Store} this
11261          */
11262         datachanged : true,
11263         /**
11264          * @event metachange
11265          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11266          * @param {Store} this
11267          * @param {Object} meta The JSON metadata
11268          */
11269         metachange : true,
11270         /**
11271          * @event add
11272          * Fires when Records have been added to the Store
11273          * @param {Store} this
11274          * @param {Roo.data.Record[]} records The array of Records added
11275          * @param {Number} index The index at which the record(s) were added
11276          */
11277         add : true,
11278         /**
11279          * @event remove
11280          * Fires when a Record has been removed from the Store
11281          * @param {Store} this
11282          * @param {Roo.data.Record} record The Record that was removed
11283          * @param {Number} index The index at which the record was removed
11284          */
11285         remove : true,
11286         /**
11287          * @event update
11288          * Fires when a Record has been updated
11289          * @param {Store} this
11290          * @param {Roo.data.Record} record The Record that was updated
11291          * @param {String} operation The update operation being performed.  Value may be one of:
11292          * <pre><code>
11293  Roo.data.Record.EDIT
11294  Roo.data.Record.REJECT
11295  Roo.data.Record.COMMIT
11296          * </code></pre>
11297          */
11298         update : true,
11299         /**
11300          * @event clear
11301          * Fires when the data cache has been cleared.
11302          * @param {Store} this
11303          */
11304         clear : true,
11305         /**
11306          * @event beforeload
11307          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11308          * the load action will be canceled.
11309          * @param {Store} this
11310          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11311          */
11312         beforeload : true,
11313         /**
11314          * @event beforeloadadd
11315          * Fires after a new set of Records has been loaded.
11316          * @param {Store} this
11317          * @param {Roo.data.Record[]} records The Records that were loaded
11318          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11319          */
11320         beforeloadadd : true,
11321         /**
11322          * @event load
11323          * Fires after a new set of Records has been loaded, before they are added to the store.
11324          * @param {Store} this
11325          * @param {Roo.data.Record[]} records The Records that were loaded
11326          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11327          * @params {Object} return from reader
11328          */
11329         load : true,
11330         /**
11331          * @event loadexception
11332          * Fires if an exception occurs in the Proxy during loading.
11333          * Called with the signature of the Proxy's "loadexception" event.
11334          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11335          * 
11336          * @param {Proxy} 
11337          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11338          * @param {Object} load options 
11339          * @param {Object} jsonData from your request (normally this contains the Exception)
11340          */
11341         loadexception : true
11342     });
11343     
11344     if(this.proxy){
11345         this.proxy = Roo.factory(this.proxy, Roo.data);
11346         this.proxy.xmodule = this.xmodule || false;
11347         this.relayEvents(this.proxy,  ["loadexception"]);
11348     }
11349     this.sortToggle = {};
11350     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11351
11352     Roo.data.Store.superclass.constructor.call(this);
11353
11354     if(this.inlineData){
11355         this.loadData(this.inlineData);
11356         delete this.inlineData;
11357     }
11358 };
11359
11360 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11361      /**
11362     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11363     * without a remote query - used by combo/forms at present.
11364     */
11365     
11366     /**
11367     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11368     */
11369     /**
11370     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11371     */
11372     /**
11373     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11374     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11375     */
11376     /**
11377     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11378     * on any HTTP request
11379     */
11380     /**
11381     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11382     */
11383     /**
11384     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11385     */
11386     multiSort: false,
11387     /**
11388     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11389     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11390     */
11391     remoteSort : false,
11392
11393     /**
11394     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11395      * loaded or when a record is removed. (defaults to false).
11396     */
11397     pruneModifiedRecords : false,
11398
11399     // private
11400     lastOptions : null,
11401
11402     /**
11403      * Add Records to the Store and fires the add event.
11404      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11405      */
11406     add : function(records){
11407         records = [].concat(records);
11408         for(var i = 0, len = records.length; i < len; i++){
11409             records[i].join(this);
11410         }
11411         var index = this.data.length;
11412         this.data.addAll(records);
11413         this.fireEvent("add", this, records, index);
11414     },
11415
11416     /**
11417      * Remove a Record from the Store and fires the remove event.
11418      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11419      */
11420     remove : function(record){
11421         var index = this.data.indexOf(record);
11422         this.data.removeAt(index);
11423  
11424         if(this.pruneModifiedRecords){
11425             this.modified.remove(record);
11426         }
11427         this.fireEvent("remove", this, record, index);
11428     },
11429
11430     /**
11431      * Remove all Records from the Store and fires the clear event.
11432      */
11433     removeAll : function(){
11434         this.data.clear();
11435         if(this.pruneModifiedRecords){
11436             this.modified = [];
11437         }
11438         this.fireEvent("clear", this);
11439     },
11440
11441     /**
11442      * Inserts Records to the Store at the given index and fires the add event.
11443      * @param {Number} index The start index at which to insert the passed Records.
11444      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11445      */
11446     insert : function(index, records){
11447         records = [].concat(records);
11448         for(var i = 0, len = records.length; i < len; i++){
11449             this.data.insert(index, records[i]);
11450             records[i].join(this);
11451         }
11452         this.fireEvent("add", this, records, index);
11453     },
11454
11455     /**
11456      * Get the index within the cache of the passed Record.
11457      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11458      * @return {Number} The index of the passed Record. Returns -1 if not found.
11459      */
11460     indexOf : function(record){
11461         return this.data.indexOf(record);
11462     },
11463
11464     /**
11465      * Get the index within the cache of the Record with the passed id.
11466      * @param {String} id The id of the Record to find.
11467      * @return {Number} The index of the Record. Returns -1 if not found.
11468      */
11469     indexOfId : function(id){
11470         return this.data.indexOfKey(id);
11471     },
11472
11473     /**
11474      * Get the Record with the specified id.
11475      * @param {String} id The id of the Record to find.
11476      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11477      */
11478     getById : function(id){
11479         return this.data.key(id);
11480     },
11481
11482     /**
11483      * Get the Record at the specified index.
11484      * @param {Number} index The index of the Record to find.
11485      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11486      */
11487     getAt : function(index){
11488         return this.data.itemAt(index);
11489     },
11490
11491     /**
11492      * Returns a range of Records between specified indices.
11493      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11494      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11495      * @return {Roo.data.Record[]} An array of Records
11496      */
11497     getRange : function(start, end){
11498         return this.data.getRange(start, end);
11499     },
11500
11501     // private
11502     storeOptions : function(o){
11503         o = Roo.apply({}, o);
11504         delete o.callback;
11505         delete o.scope;
11506         this.lastOptions = o;
11507     },
11508
11509     /**
11510      * Loads the Record cache from the configured Proxy using the configured Reader.
11511      * <p>
11512      * If using remote paging, then the first load call must specify the <em>start</em>
11513      * and <em>limit</em> properties in the options.params property to establish the initial
11514      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11515      * <p>
11516      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11517      * and this call will return before the new data has been loaded. Perform any post-processing
11518      * in a callback function, or in a "load" event handler.</strong>
11519      * <p>
11520      * @param {Object} options An object containing properties which control loading options:<ul>
11521      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11522      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11523      * passed the following arguments:<ul>
11524      * <li>r : Roo.data.Record[]</li>
11525      * <li>options: Options object from the load call</li>
11526      * <li>success: Boolean success indicator</li></ul></li>
11527      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11528      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11529      * </ul>
11530      */
11531     load : function(options){
11532         options = options || {};
11533         if(this.fireEvent("beforeload", this, options) !== false){
11534             this.storeOptions(options);
11535             var p = Roo.apply(options.params || {}, this.baseParams);
11536             // if meta was not loaded from remote source.. try requesting it.
11537             if (!this.reader.metaFromRemote) {
11538                 p._requestMeta = 1;
11539             }
11540             if(this.sortInfo && this.remoteSort){
11541                 var pn = this.paramNames;
11542                 p[pn["sort"]] = this.sortInfo.field;
11543                 p[pn["dir"]] = this.sortInfo.direction;
11544             }
11545             if (this.multiSort) {
11546                 var pn = this.paramNames;
11547                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11548             }
11549             
11550             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11551         }
11552     },
11553
11554     /**
11555      * Reloads the Record cache from the configured Proxy using the configured Reader and
11556      * the options from the last load operation performed.
11557      * @param {Object} options (optional) An object containing properties which may override the options
11558      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11559      * the most recently used options are reused).
11560      */
11561     reload : function(options){
11562         this.load(Roo.applyIf(options||{}, this.lastOptions));
11563     },
11564
11565     // private
11566     // Called as a callback by the Reader during a load operation.
11567     loadRecords : function(o, options, success){
11568         if(!o || success === false){
11569             if(success !== false){
11570                 this.fireEvent("load", this, [], options, o);
11571             }
11572             if(options.callback){
11573                 options.callback.call(options.scope || this, [], options, false);
11574             }
11575             return;
11576         }
11577         // if data returned failure - throw an exception.
11578         if (o.success === false) {
11579             // show a message if no listener is registered.
11580             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11581                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11582             }
11583             // loadmask wil be hooked into this..
11584             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11585             return;
11586         }
11587         var r = o.records, t = o.totalRecords || r.length;
11588         
11589         this.fireEvent("beforeloadadd", this, r, options, o);
11590         
11591         if(!options || options.add !== true){
11592             if(this.pruneModifiedRecords){
11593                 this.modified = [];
11594             }
11595             for(var i = 0, len = r.length; i < len; i++){
11596                 r[i].join(this);
11597             }
11598             if(this.snapshot){
11599                 this.data = this.snapshot;
11600                 delete this.snapshot;
11601             }
11602             this.data.clear();
11603             this.data.addAll(r);
11604             this.totalLength = t;
11605             this.applySort();
11606             this.fireEvent("datachanged", this);
11607         }else{
11608             this.totalLength = Math.max(t, this.data.length+r.length);
11609             this.add(r);
11610         }
11611         
11612         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11613                 
11614             var e = new Roo.data.Record({});
11615
11616             e.set(this.parent.displayField, this.parent.emptyTitle);
11617             e.set(this.parent.valueField, '');
11618
11619             this.insert(0, e);
11620         }
11621             
11622         this.fireEvent("load", this, r, options, o);
11623         if(options.callback){
11624             options.callback.call(options.scope || this, r, options, true);
11625         }
11626     },
11627
11628
11629     /**
11630      * Loads data from a passed data block. A Reader which understands the format of the data
11631      * must have been configured in the constructor.
11632      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11633      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11634      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11635      */
11636     loadData : function(o, append){
11637         var r = this.reader.readRecords(o);
11638         this.loadRecords(r, {add: append}, true);
11639     },
11640
11641     /**
11642      * Gets the number of cached records.
11643      * <p>
11644      * <em>If using paging, this may not be the total size of the dataset. If the data object
11645      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11646      * the data set size</em>
11647      */
11648     getCount : function(){
11649         return this.data.length || 0;
11650     },
11651
11652     /**
11653      * Gets the total number of records in the dataset as returned by the server.
11654      * <p>
11655      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11656      * the dataset size</em>
11657      */
11658     getTotalCount : function(){
11659         return this.totalLength || 0;
11660     },
11661
11662     /**
11663      * Returns the sort state of the Store as an object with two properties:
11664      * <pre><code>
11665  field {String} The name of the field by which the Records are sorted
11666  direction {String} The sort order, "ASC" or "DESC"
11667      * </code></pre>
11668      */
11669     getSortState : function(){
11670         return this.sortInfo;
11671     },
11672
11673     // private
11674     applySort : function(){
11675         if(this.sortInfo && !this.remoteSort){
11676             var s = this.sortInfo, f = s.field;
11677             var st = this.fields.get(f).sortType;
11678             var fn = function(r1, r2){
11679                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11680                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11681             };
11682             this.data.sort(s.direction, fn);
11683             if(this.snapshot && this.snapshot != this.data){
11684                 this.snapshot.sort(s.direction, fn);
11685             }
11686         }
11687     },
11688
11689     /**
11690      * Sets the default sort column and order to be used by the next load operation.
11691      * @param {String} fieldName The name of the field to sort by.
11692      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11693      */
11694     setDefaultSort : function(field, dir){
11695         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11696     },
11697
11698     /**
11699      * Sort the Records.
11700      * If remote sorting is used, the sort is performed on the server, and the cache is
11701      * reloaded. If local sorting is used, the cache is sorted internally.
11702      * @param {String} fieldName The name of the field to sort by.
11703      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11704      */
11705     sort : function(fieldName, dir){
11706         var f = this.fields.get(fieldName);
11707         if(!dir){
11708             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11709             
11710             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11711                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11712             }else{
11713                 dir = f.sortDir;
11714             }
11715         }
11716         this.sortToggle[f.name] = dir;
11717         this.sortInfo = {field: f.name, direction: dir};
11718         if(!this.remoteSort){
11719             this.applySort();
11720             this.fireEvent("datachanged", this);
11721         }else{
11722             this.load(this.lastOptions);
11723         }
11724     },
11725
11726     /**
11727      * Calls the specified function for each of the Records in the cache.
11728      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11729      * Returning <em>false</em> aborts and exits the iteration.
11730      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11731      */
11732     each : function(fn, scope){
11733         this.data.each(fn, scope);
11734     },
11735
11736     /**
11737      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11738      * (e.g., during paging).
11739      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11740      */
11741     getModifiedRecords : function(){
11742         return this.modified;
11743     },
11744
11745     // private
11746     createFilterFn : function(property, value, anyMatch){
11747         if(!value.exec){ // not a regex
11748             value = String(value);
11749             if(value.length == 0){
11750                 return false;
11751             }
11752             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11753         }
11754         return function(r){
11755             return value.test(r.data[property]);
11756         };
11757     },
11758
11759     /**
11760      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11761      * @param {String} property A field on your records
11762      * @param {Number} start The record index to start at (defaults to 0)
11763      * @param {Number} end The last record index to include (defaults to length - 1)
11764      * @return {Number} The sum
11765      */
11766     sum : function(property, start, end){
11767         var rs = this.data.items, v = 0;
11768         start = start || 0;
11769         end = (end || end === 0) ? end : rs.length-1;
11770
11771         for(var i = start; i <= end; i++){
11772             v += (rs[i].data[property] || 0);
11773         }
11774         return v;
11775     },
11776
11777     /**
11778      * Filter the records by a specified property.
11779      * @param {String} field A field on your records
11780      * @param {String/RegExp} value Either a string that the field
11781      * should start with or a RegExp to test against the field
11782      * @param {Boolean} anyMatch True to match any part not just the beginning
11783      */
11784     filter : function(property, value, anyMatch){
11785         var fn = this.createFilterFn(property, value, anyMatch);
11786         return fn ? this.filterBy(fn) : this.clearFilter();
11787     },
11788
11789     /**
11790      * Filter by a function. The specified function will be called with each
11791      * record in this data source. If the function returns true the record is included,
11792      * otherwise it is filtered.
11793      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11794      * @param {Object} scope (optional) The scope of the function (defaults to this)
11795      */
11796     filterBy : function(fn, scope){
11797         this.snapshot = this.snapshot || this.data;
11798         this.data = this.queryBy(fn, scope||this);
11799         this.fireEvent("datachanged", this);
11800     },
11801
11802     /**
11803      * Query the records by a specified property.
11804      * @param {String} field A field on your records
11805      * @param {String/RegExp} value Either a string that the field
11806      * should start with or a RegExp to test against the field
11807      * @param {Boolean} anyMatch True to match any part not just the beginning
11808      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11809      */
11810     query : function(property, value, anyMatch){
11811         var fn = this.createFilterFn(property, value, anyMatch);
11812         return fn ? this.queryBy(fn) : this.data.clone();
11813     },
11814
11815     /**
11816      * Query by a function. The specified function will be called with each
11817      * record in this data source. If the function returns true the record is included
11818      * in the results.
11819      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11820      * @param {Object} scope (optional) The scope of the function (defaults to this)
11821       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11822      **/
11823     queryBy : function(fn, scope){
11824         var data = this.snapshot || this.data;
11825         return data.filterBy(fn, scope||this);
11826     },
11827
11828     /**
11829      * Collects unique values for a particular dataIndex from this store.
11830      * @param {String} dataIndex The property to collect
11831      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11832      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11833      * @return {Array} An array of the unique values
11834      **/
11835     collect : function(dataIndex, allowNull, bypassFilter){
11836         var d = (bypassFilter === true && this.snapshot) ?
11837                 this.snapshot.items : this.data.items;
11838         var v, sv, r = [], l = {};
11839         for(var i = 0, len = d.length; i < len; i++){
11840             v = d[i].data[dataIndex];
11841             sv = String(v);
11842             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11843                 l[sv] = true;
11844                 r[r.length] = v;
11845             }
11846         }
11847         return r;
11848     },
11849
11850     /**
11851      * Revert to a view of the Record cache with no filtering applied.
11852      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11853      */
11854     clearFilter : function(suppressEvent){
11855         if(this.snapshot && this.snapshot != this.data){
11856             this.data = this.snapshot;
11857             delete this.snapshot;
11858             if(suppressEvent !== true){
11859                 this.fireEvent("datachanged", this);
11860             }
11861         }
11862     },
11863
11864     // private
11865     afterEdit : function(record){
11866         if(this.modified.indexOf(record) == -1){
11867             this.modified.push(record);
11868         }
11869         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11870     },
11871     
11872     // private
11873     afterReject : function(record){
11874         this.modified.remove(record);
11875         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11876     },
11877
11878     // private
11879     afterCommit : function(record){
11880         this.modified.remove(record);
11881         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11882     },
11883
11884     /**
11885      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11886      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11887      */
11888     commitChanges : function(){
11889         var m = this.modified.slice(0);
11890         this.modified = [];
11891         for(var i = 0, len = m.length; i < len; i++){
11892             m[i].commit();
11893         }
11894     },
11895
11896     /**
11897      * Cancel outstanding changes on all changed records.
11898      */
11899     rejectChanges : function(){
11900         var m = this.modified.slice(0);
11901         this.modified = [];
11902         for(var i = 0, len = m.length; i < len; i++){
11903             m[i].reject();
11904         }
11905     },
11906
11907     onMetaChange : function(meta, rtype, o){
11908         this.recordType = rtype;
11909         this.fields = rtype.prototype.fields;
11910         delete this.snapshot;
11911         this.sortInfo = meta.sortInfo || this.sortInfo;
11912         this.modified = [];
11913         this.fireEvent('metachange', this, this.reader.meta);
11914     },
11915     
11916     moveIndex : function(data, type)
11917     {
11918         var index = this.indexOf(data);
11919         
11920         var newIndex = index + type;
11921         
11922         this.remove(data);
11923         
11924         this.insert(newIndex, data);
11925         
11926     }
11927 });/*
11928  * Based on:
11929  * Ext JS Library 1.1.1
11930  * Copyright(c) 2006-2007, Ext JS, LLC.
11931  *
11932  * Originally Released Under LGPL - original licence link has changed is not relivant.
11933  *
11934  * Fork - LGPL
11935  * <script type="text/javascript">
11936  */
11937
11938 /**
11939  * @class Roo.data.SimpleStore
11940  * @extends Roo.data.Store
11941  * Small helper class to make creating Stores from Array data easier.
11942  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11943  * @cfg {Array} fields An array of field definition objects, or field name strings.
11944  * @cfg {Array} data The multi-dimensional array of data
11945  * @constructor
11946  * @param {Object} config
11947  */
11948 Roo.data.SimpleStore = function(config){
11949     Roo.data.SimpleStore.superclass.constructor.call(this, {
11950         isLocal : true,
11951         reader: new Roo.data.ArrayReader({
11952                 id: config.id
11953             },
11954             Roo.data.Record.create(config.fields)
11955         ),
11956         proxy : new Roo.data.MemoryProxy(config.data)
11957     });
11958     this.load();
11959 };
11960 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11961  * Based on:
11962  * Ext JS Library 1.1.1
11963  * Copyright(c) 2006-2007, Ext JS, LLC.
11964  *
11965  * Originally Released Under LGPL - original licence link has changed is not relivant.
11966  *
11967  * Fork - LGPL
11968  * <script type="text/javascript">
11969  */
11970
11971 /**
11972 /**
11973  * @extends Roo.data.Store
11974  * @class Roo.data.JsonStore
11975  * Small helper class to make creating Stores for JSON data easier. <br/>
11976 <pre><code>
11977 var store = new Roo.data.JsonStore({
11978     url: 'get-images.php',
11979     root: 'images',
11980     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11981 });
11982 </code></pre>
11983  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11984  * JsonReader and HttpProxy (unless inline data is provided).</b>
11985  * @cfg {Array} fields An array of field definition objects, or field name strings.
11986  * @constructor
11987  * @param {Object} config
11988  */
11989 Roo.data.JsonStore = function(c){
11990     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11991         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11992         reader: new Roo.data.JsonReader(c, c.fields)
11993     }));
11994 };
11995 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11996  * Based on:
11997  * Ext JS Library 1.1.1
11998  * Copyright(c) 2006-2007, Ext JS, LLC.
11999  *
12000  * Originally Released Under LGPL - original licence link has changed is not relivant.
12001  *
12002  * Fork - LGPL
12003  * <script type="text/javascript">
12004  */
12005
12006  
12007 Roo.data.Field = function(config){
12008     if(typeof config == "string"){
12009         config = {name: config};
12010     }
12011     Roo.apply(this, config);
12012     
12013     if(!this.type){
12014         this.type = "auto";
12015     }
12016     
12017     var st = Roo.data.SortTypes;
12018     // named sortTypes are supported, here we look them up
12019     if(typeof this.sortType == "string"){
12020         this.sortType = st[this.sortType];
12021     }
12022     
12023     // set default sortType for strings and dates
12024     if(!this.sortType){
12025         switch(this.type){
12026             case "string":
12027                 this.sortType = st.asUCString;
12028                 break;
12029             case "date":
12030                 this.sortType = st.asDate;
12031                 break;
12032             default:
12033                 this.sortType = st.none;
12034         }
12035     }
12036
12037     // define once
12038     var stripRe = /[\$,%]/g;
12039
12040     // prebuilt conversion function for this field, instead of
12041     // switching every time we're reading a value
12042     if(!this.convert){
12043         var cv, dateFormat = this.dateFormat;
12044         switch(this.type){
12045             case "":
12046             case "auto":
12047             case undefined:
12048                 cv = function(v){ return v; };
12049                 break;
12050             case "string":
12051                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12052                 break;
12053             case "int":
12054                 cv = function(v){
12055                     return v !== undefined && v !== null && v !== '' ?
12056                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12057                     };
12058                 break;
12059             case "float":
12060                 cv = function(v){
12061                     return v !== undefined && v !== null && v !== '' ?
12062                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12063                     };
12064                 break;
12065             case "bool":
12066             case "boolean":
12067                 cv = function(v){ return v === true || v === "true" || v == 1; };
12068                 break;
12069             case "date":
12070                 cv = function(v){
12071                     if(!v){
12072                         return '';
12073                     }
12074                     if(v instanceof Date){
12075                         return v;
12076                     }
12077                     if(dateFormat){
12078                         if(dateFormat == "timestamp"){
12079                             return new Date(v*1000);
12080                         }
12081                         return Date.parseDate(v, dateFormat);
12082                     }
12083                     var parsed = Date.parse(v);
12084                     return parsed ? new Date(parsed) : null;
12085                 };
12086              break;
12087             
12088         }
12089         this.convert = cv;
12090     }
12091 };
12092
12093 Roo.data.Field.prototype = {
12094     dateFormat: null,
12095     defaultValue: "",
12096     mapping: null,
12097     sortType : null,
12098     sortDir : "ASC"
12099 };/*
12100  * Based on:
12101  * Ext JS Library 1.1.1
12102  * Copyright(c) 2006-2007, Ext JS, LLC.
12103  *
12104  * Originally Released Under LGPL - original licence link has changed is not relivant.
12105  *
12106  * Fork - LGPL
12107  * <script type="text/javascript">
12108  */
12109  
12110 // Base class for reading structured data from a data source.  This class is intended to be
12111 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12112
12113 /**
12114  * @class Roo.data.DataReader
12115  * Base class for reading structured data from a data source.  This class is intended to be
12116  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12117  */
12118
12119 Roo.data.DataReader = function(meta, recordType){
12120     
12121     this.meta = meta;
12122     
12123     this.recordType = recordType instanceof Array ? 
12124         Roo.data.Record.create(recordType) : recordType;
12125 };
12126
12127 Roo.data.DataReader.prototype = {
12128      /**
12129      * Create an empty record
12130      * @param {Object} data (optional) - overlay some values
12131      * @return {Roo.data.Record} record created.
12132      */
12133     newRow :  function(d) {
12134         var da =  {};
12135         this.recordType.prototype.fields.each(function(c) {
12136             switch( c.type) {
12137                 case 'int' : da[c.name] = 0; break;
12138                 case 'date' : da[c.name] = new Date(); break;
12139                 case 'float' : da[c.name] = 0.0; break;
12140                 case 'boolean' : da[c.name] = false; break;
12141                 default : da[c.name] = ""; break;
12142             }
12143             
12144         });
12145         return new this.recordType(Roo.apply(da, d));
12146     }
12147     
12148 };/*
12149  * Based on:
12150  * Ext JS Library 1.1.1
12151  * Copyright(c) 2006-2007, Ext JS, LLC.
12152  *
12153  * Originally Released Under LGPL - original licence link has changed is not relivant.
12154  *
12155  * Fork - LGPL
12156  * <script type="text/javascript">
12157  */
12158
12159 /**
12160  * @class Roo.data.DataProxy
12161  * @extends Roo.data.Observable
12162  * This class is an abstract base class for implementations which provide retrieval of
12163  * unformatted data objects.<br>
12164  * <p>
12165  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12166  * (of the appropriate type which knows how to parse the data object) to provide a block of
12167  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12168  * <p>
12169  * Custom implementations must implement the load method as described in
12170  * {@link Roo.data.HttpProxy#load}.
12171  */
12172 Roo.data.DataProxy = function(){
12173     this.addEvents({
12174         /**
12175          * @event beforeload
12176          * Fires before a network request is made to retrieve a data object.
12177          * @param {Object} This DataProxy object.
12178          * @param {Object} params The params parameter to the load function.
12179          */
12180         beforeload : true,
12181         /**
12182          * @event load
12183          * Fires before the load method's callback is called.
12184          * @param {Object} This DataProxy object.
12185          * @param {Object} o The data object.
12186          * @param {Object} arg The callback argument object passed to the load function.
12187          */
12188         load : true,
12189         /**
12190          * @event loadexception
12191          * Fires if an Exception occurs during data retrieval.
12192          * @param {Object} This DataProxy object.
12193          * @param {Object} o The data object.
12194          * @param {Object} arg The callback argument object passed to the load function.
12195          * @param {Object} e The Exception.
12196          */
12197         loadexception : true
12198     });
12199     Roo.data.DataProxy.superclass.constructor.call(this);
12200 };
12201
12202 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12203
12204     /**
12205      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12206      */
12207 /*
12208  * Based on:
12209  * Ext JS Library 1.1.1
12210  * Copyright(c) 2006-2007, Ext JS, LLC.
12211  *
12212  * Originally Released Under LGPL - original licence link has changed is not relivant.
12213  *
12214  * Fork - LGPL
12215  * <script type="text/javascript">
12216  */
12217 /**
12218  * @class Roo.data.MemoryProxy
12219  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12220  * to the Reader when its load method is called.
12221  * @constructor
12222  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12223  */
12224 Roo.data.MemoryProxy = function(data){
12225     if (data.data) {
12226         data = data.data;
12227     }
12228     Roo.data.MemoryProxy.superclass.constructor.call(this);
12229     this.data = data;
12230 };
12231
12232 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12233     
12234     /**
12235      * Load data from the requested source (in this case an in-memory
12236      * data object passed to the constructor), read the data object into
12237      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12238      * process that block using the passed callback.
12239      * @param {Object} params This parameter is not used by the MemoryProxy class.
12240      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12241      * object into a block of Roo.data.Records.
12242      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12243      * The function must be passed <ul>
12244      * <li>The Record block object</li>
12245      * <li>The "arg" argument from the load function</li>
12246      * <li>A boolean success indicator</li>
12247      * </ul>
12248      * @param {Object} scope The scope in which to call the callback
12249      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12250      */
12251     load : function(params, reader, callback, scope, arg){
12252         params = params || {};
12253         var result;
12254         try {
12255             result = reader.readRecords(this.data);
12256         }catch(e){
12257             this.fireEvent("loadexception", this, arg, null, e);
12258             callback.call(scope, null, arg, false);
12259             return;
12260         }
12261         callback.call(scope, result, arg, true);
12262     },
12263     
12264     // private
12265     update : function(params, records){
12266         
12267     }
12268 });/*
12269  * Based on:
12270  * Ext JS Library 1.1.1
12271  * Copyright(c) 2006-2007, Ext JS, LLC.
12272  *
12273  * Originally Released Under LGPL - original licence link has changed is not relivant.
12274  *
12275  * Fork - LGPL
12276  * <script type="text/javascript">
12277  */
12278 /**
12279  * @class Roo.data.HttpProxy
12280  * @extends Roo.data.DataProxy
12281  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12282  * configured to reference a certain URL.<br><br>
12283  * <p>
12284  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12285  * from which the running page was served.<br><br>
12286  * <p>
12287  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12288  * <p>
12289  * Be aware that to enable the browser to parse an XML document, the server must set
12290  * the Content-Type header in the HTTP response to "text/xml".
12291  * @constructor
12292  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12293  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12294  * will be used to make the request.
12295  */
12296 Roo.data.HttpProxy = function(conn){
12297     Roo.data.HttpProxy.superclass.constructor.call(this);
12298     // is conn a conn config or a real conn?
12299     this.conn = conn;
12300     this.useAjax = !conn || !conn.events;
12301   
12302 };
12303
12304 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12305     // thse are take from connection...
12306     
12307     /**
12308      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12309      */
12310     /**
12311      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12312      * extra parameters to each request made by this object. (defaults to undefined)
12313      */
12314     /**
12315      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12316      *  to each request made by this object. (defaults to undefined)
12317      */
12318     /**
12319      * @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)
12320      */
12321     /**
12322      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12323      */
12324      /**
12325      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12326      * @type Boolean
12327      */
12328   
12329
12330     /**
12331      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12332      * @type Boolean
12333      */
12334     /**
12335      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12336      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12337      * a finer-grained basis than the DataProxy events.
12338      */
12339     getConnection : function(){
12340         return this.useAjax ? Roo.Ajax : this.conn;
12341     },
12342
12343     /**
12344      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12345      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12346      * process that block using the passed callback.
12347      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12348      * for the request to the remote server.
12349      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12350      * object into a block of Roo.data.Records.
12351      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12352      * The function must be passed <ul>
12353      * <li>The Record block object</li>
12354      * <li>The "arg" argument from the load function</li>
12355      * <li>A boolean success indicator</li>
12356      * </ul>
12357      * @param {Object} scope The scope in which to call the callback
12358      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12359      */
12360     load : function(params, reader, callback, scope, arg){
12361         if(this.fireEvent("beforeload", this, params) !== false){
12362             var  o = {
12363                 params : params || {},
12364                 request: {
12365                     callback : callback,
12366                     scope : scope,
12367                     arg : arg
12368                 },
12369                 reader: reader,
12370                 callback : this.loadResponse,
12371                 scope: this
12372             };
12373             if(this.useAjax){
12374                 Roo.applyIf(o, this.conn);
12375                 if(this.activeRequest){
12376                     Roo.Ajax.abort(this.activeRequest);
12377                 }
12378                 this.activeRequest = Roo.Ajax.request(o);
12379             }else{
12380                 this.conn.request(o);
12381             }
12382         }else{
12383             callback.call(scope||this, null, arg, false);
12384         }
12385     },
12386
12387     // private
12388     loadResponse : function(o, success, response){
12389         delete this.activeRequest;
12390         if(!success){
12391             this.fireEvent("loadexception", this, o, response);
12392             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12393             return;
12394         }
12395         var result;
12396         try {
12397             result = o.reader.read(response);
12398         }catch(e){
12399             this.fireEvent("loadexception", this, o, response, e);
12400             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12401             return;
12402         }
12403         
12404         this.fireEvent("load", this, o, o.request.arg);
12405         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12406     },
12407
12408     // private
12409     update : function(dataSet){
12410
12411     },
12412
12413     // private
12414     updateResponse : function(dataSet){
12415
12416     }
12417 });/*
12418  * Based on:
12419  * Ext JS Library 1.1.1
12420  * Copyright(c) 2006-2007, Ext JS, LLC.
12421  *
12422  * Originally Released Under LGPL - original licence link has changed is not relivant.
12423  *
12424  * Fork - LGPL
12425  * <script type="text/javascript">
12426  */
12427
12428 /**
12429  * @class Roo.data.ScriptTagProxy
12430  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12431  * other than the originating domain of the running page.<br><br>
12432  * <p>
12433  * <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
12434  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12435  * <p>
12436  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12437  * source code that is used as the source inside a &lt;script> tag.<br><br>
12438  * <p>
12439  * In order for the browser to process the returned data, the server must wrap the data object
12440  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12441  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12442  * depending on whether the callback name was passed:
12443  * <p>
12444  * <pre><code>
12445 boolean scriptTag = false;
12446 String cb = request.getParameter("callback");
12447 if (cb != null) {
12448     scriptTag = true;
12449     response.setContentType("text/javascript");
12450 } else {
12451     response.setContentType("application/x-json");
12452 }
12453 Writer out = response.getWriter();
12454 if (scriptTag) {
12455     out.write(cb + "(");
12456 }
12457 out.print(dataBlock.toJsonString());
12458 if (scriptTag) {
12459     out.write(");");
12460 }
12461 </pre></code>
12462  *
12463  * @constructor
12464  * @param {Object} config A configuration object.
12465  */
12466 Roo.data.ScriptTagProxy = function(config){
12467     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12468     Roo.apply(this, config);
12469     this.head = document.getElementsByTagName("head")[0];
12470 };
12471
12472 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12473
12474 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12475     /**
12476      * @cfg {String} url The URL from which to request the data object.
12477      */
12478     /**
12479      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12480      */
12481     timeout : 30000,
12482     /**
12483      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12484      * the server the name of the callback function set up by the load call to process the returned data object.
12485      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12486      * javascript output which calls this named function passing the data object as its only parameter.
12487      */
12488     callbackParam : "callback",
12489     /**
12490      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12491      * name to the request.
12492      */
12493     nocache : true,
12494
12495     /**
12496      * Load data from the configured URL, read the data object into
12497      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12498      * process that block using the passed callback.
12499      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12500      * for the request to the remote server.
12501      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12502      * object into a block of Roo.data.Records.
12503      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12504      * The function must be passed <ul>
12505      * <li>The Record block object</li>
12506      * <li>The "arg" argument from the load function</li>
12507      * <li>A boolean success indicator</li>
12508      * </ul>
12509      * @param {Object} scope The scope in which to call the callback
12510      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12511      */
12512     load : function(params, reader, callback, scope, arg){
12513         if(this.fireEvent("beforeload", this, params) !== false){
12514
12515             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12516
12517             var url = this.url;
12518             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12519             if(this.nocache){
12520                 url += "&_dc=" + (new Date().getTime());
12521             }
12522             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12523             var trans = {
12524                 id : transId,
12525                 cb : "stcCallback"+transId,
12526                 scriptId : "stcScript"+transId,
12527                 params : params,
12528                 arg : arg,
12529                 url : url,
12530                 callback : callback,
12531                 scope : scope,
12532                 reader : reader
12533             };
12534             var conn = this;
12535
12536             window[trans.cb] = function(o){
12537                 conn.handleResponse(o, trans);
12538             };
12539
12540             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12541
12542             if(this.autoAbort !== false){
12543                 this.abort();
12544             }
12545
12546             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12547
12548             var script = document.createElement("script");
12549             script.setAttribute("src", url);
12550             script.setAttribute("type", "text/javascript");
12551             script.setAttribute("id", trans.scriptId);
12552             this.head.appendChild(script);
12553
12554             this.trans = trans;
12555         }else{
12556             callback.call(scope||this, null, arg, false);
12557         }
12558     },
12559
12560     // private
12561     isLoading : function(){
12562         return this.trans ? true : false;
12563     },
12564
12565     /**
12566      * Abort the current server request.
12567      */
12568     abort : function(){
12569         if(this.isLoading()){
12570             this.destroyTrans(this.trans);
12571         }
12572     },
12573
12574     // private
12575     destroyTrans : function(trans, isLoaded){
12576         this.head.removeChild(document.getElementById(trans.scriptId));
12577         clearTimeout(trans.timeoutId);
12578         if(isLoaded){
12579             window[trans.cb] = undefined;
12580             try{
12581                 delete window[trans.cb];
12582             }catch(e){}
12583         }else{
12584             // if hasn't been loaded, wait for load to remove it to prevent script error
12585             window[trans.cb] = function(){
12586                 window[trans.cb] = undefined;
12587                 try{
12588                     delete window[trans.cb];
12589                 }catch(e){}
12590             };
12591         }
12592     },
12593
12594     // private
12595     handleResponse : function(o, trans){
12596         this.trans = false;
12597         this.destroyTrans(trans, true);
12598         var result;
12599         try {
12600             result = trans.reader.readRecords(o);
12601         }catch(e){
12602             this.fireEvent("loadexception", this, o, trans.arg, e);
12603             trans.callback.call(trans.scope||window, null, trans.arg, false);
12604             return;
12605         }
12606         this.fireEvent("load", this, o, trans.arg);
12607         trans.callback.call(trans.scope||window, result, trans.arg, true);
12608     },
12609
12610     // private
12611     handleFailure : function(trans){
12612         this.trans = false;
12613         this.destroyTrans(trans, false);
12614         this.fireEvent("loadexception", this, null, trans.arg);
12615         trans.callback.call(trans.scope||window, null, trans.arg, false);
12616     }
12617 });/*
12618  * Based on:
12619  * Ext JS Library 1.1.1
12620  * Copyright(c) 2006-2007, Ext JS, LLC.
12621  *
12622  * Originally Released Under LGPL - original licence link has changed is not relivant.
12623  *
12624  * Fork - LGPL
12625  * <script type="text/javascript">
12626  */
12627
12628 /**
12629  * @class Roo.data.JsonReader
12630  * @extends Roo.data.DataReader
12631  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12632  * based on mappings in a provided Roo.data.Record constructor.
12633  * 
12634  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12635  * in the reply previously. 
12636  * 
12637  * <p>
12638  * Example code:
12639  * <pre><code>
12640 var RecordDef = Roo.data.Record.create([
12641     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12642     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12643 ]);
12644 var myReader = new Roo.data.JsonReader({
12645     totalProperty: "results",    // The property which contains the total dataset size (optional)
12646     root: "rows",                // The property which contains an Array of row objects
12647     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12648 }, RecordDef);
12649 </code></pre>
12650  * <p>
12651  * This would consume a JSON file like this:
12652  * <pre><code>
12653 { 'results': 2, 'rows': [
12654     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12655     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12656 }
12657 </code></pre>
12658  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12659  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12660  * paged from the remote server.
12661  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12662  * @cfg {String} root name of the property which contains the Array of row objects.
12663  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12664  * @cfg {Array} fields Array of field definition objects
12665  * @constructor
12666  * Create a new JsonReader
12667  * @param {Object} meta Metadata configuration options
12668  * @param {Object} recordType Either an Array of field definition objects,
12669  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12670  */
12671 Roo.data.JsonReader = function(meta, recordType){
12672     
12673     meta = meta || {};
12674     // set some defaults:
12675     Roo.applyIf(meta, {
12676         totalProperty: 'total',
12677         successProperty : 'success',
12678         root : 'data',
12679         id : 'id'
12680     });
12681     
12682     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12683 };
12684 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12685     
12686     /**
12687      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12688      * Used by Store query builder to append _requestMeta to params.
12689      * 
12690      */
12691     metaFromRemote : false,
12692     /**
12693      * This method is only used by a DataProxy which has retrieved data from a remote server.
12694      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12695      * @return {Object} data A data block which is used by an Roo.data.Store object as
12696      * a cache of Roo.data.Records.
12697      */
12698     read : function(response){
12699         var json = response.responseText;
12700        
12701         var o = /* eval:var:o */ eval("("+json+")");
12702         if(!o) {
12703             throw {message: "JsonReader.read: Json object not found"};
12704         }
12705         
12706         if(o.metaData){
12707             
12708             delete this.ef;
12709             this.metaFromRemote = true;
12710             this.meta = o.metaData;
12711             this.recordType = Roo.data.Record.create(o.metaData.fields);
12712             this.onMetaChange(this.meta, this.recordType, o);
12713         }
12714         return this.readRecords(o);
12715     },
12716
12717     // private function a store will implement
12718     onMetaChange : function(meta, recordType, o){
12719
12720     },
12721
12722     /**
12723          * @ignore
12724          */
12725     simpleAccess: function(obj, subsc) {
12726         return obj[subsc];
12727     },
12728
12729         /**
12730          * @ignore
12731          */
12732     getJsonAccessor: function(){
12733         var re = /[\[\.]/;
12734         return function(expr) {
12735             try {
12736                 return(re.test(expr))
12737                     ? new Function("obj", "return obj." + expr)
12738                     : function(obj){
12739                         return obj[expr];
12740                     };
12741             } catch(e){}
12742             return Roo.emptyFn;
12743         };
12744     }(),
12745
12746     /**
12747      * Create a data block containing Roo.data.Records from an XML document.
12748      * @param {Object} o An object which contains an Array of row objects in the property specified
12749      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12750      * which contains the total size of the dataset.
12751      * @return {Object} data A data block which is used by an Roo.data.Store object as
12752      * a cache of Roo.data.Records.
12753      */
12754     readRecords : function(o){
12755         /**
12756          * After any data loads, the raw JSON data is available for further custom processing.
12757          * @type Object
12758          */
12759         this.o = o;
12760         var s = this.meta, Record = this.recordType,
12761             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12762
12763 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12764         if (!this.ef) {
12765             if(s.totalProperty) {
12766                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12767                 }
12768                 if(s.successProperty) {
12769                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12770                 }
12771                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12772                 if (s.id) {
12773                         var g = this.getJsonAccessor(s.id);
12774                         this.getId = function(rec) {
12775                                 var r = g(rec);  
12776                                 return (r === undefined || r === "") ? null : r;
12777                         };
12778                 } else {
12779                         this.getId = function(){return null;};
12780                 }
12781             this.ef = [];
12782             for(var jj = 0; jj < fl; jj++){
12783                 f = fi[jj];
12784                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12785                 this.ef[jj] = this.getJsonAccessor(map);
12786             }
12787         }
12788
12789         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12790         if(s.totalProperty){
12791             var vt = parseInt(this.getTotal(o), 10);
12792             if(!isNaN(vt)){
12793                 totalRecords = vt;
12794             }
12795         }
12796         if(s.successProperty){
12797             var vs = this.getSuccess(o);
12798             if(vs === false || vs === 'false'){
12799                 success = false;
12800             }
12801         }
12802         var records = [];
12803         for(var i = 0; i < c; i++){
12804                 var n = root[i];
12805             var values = {};
12806             var id = this.getId(n);
12807             for(var j = 0; j < fl; j++){
12808                 f = fi[j];
12809             var v = this.ef[j](n);
12810             if (!f.convert) {
12811                 Roo.log('missing convert for ' + f.name);
12812                 Roo.log(f);
12813                 continue;
12814             }
12815             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12816             }
12817             var record = new Record(values, id);
12818             record.json = n;
12819             records[i] = record;
12820         }
12821         return {
12822             raw : o,
12823             success : success,
12824             records : records,
12825             totalRecords : totalRecords
12826         };
12827     }
12828 });/*
12829  * Based on:
12830  * Ext JS Library 1.1.1
12831  * Copyright(c) 2006-2007, Ext JS, LLC.
12832  *
12833  * Originally Released Under LGPL - original licence link has changed is not relivant.
12834  *
12835  * Fork - LGPL
12836  * <script type="text/javascript">
12837  */
12838
12839 /**
12840  * @class Roo.data.ArrayReader
12841  * @extends Roo.data.DataReader
12842  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12843  * Each element of that Array represents a row of data fields. The
12844  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12845  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12846  * <p>
12847  * Example code:.
12848  * <pre><code>
12849 var RecordDef = Roo.data.Record.create([
12850     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12851     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12852 ]);
12853 var myReader = new Roo.data.ArrayReader({
12854     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12855 }, RecordDef);
12856 </code></pre>
12857  * <p>
12858  * This would consume an Array like this:
12859  * <pre><code>
12860 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12861   </code></pre>
12862  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12863  * @constructor
12864  * Create a new JsonReader
12865  * @param {Object} meta Metadata configuration options.
12866  * @param {Object} recordType Either an Array of field definition objects
12867  * as specified to {@link Roo.data.Record#create},
12868  * or an {@link Roo.data.Record} object
12869  * created using {@link Roo.data.Record#create}.
12870  */
12871 Roo.data.ArrayReader = function(meta, recordType){
12872     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12873 };
12874
12875 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12876     /**
12877      * Create a data block containing Roo.data.Records from an XML document.
12878      * @param {Object} o An Array of row objects which represents the dataset.
12879      * @return {Object} data A data block which is used by an Roo.data.Store object as
12880      * a cache of Roo.data.Records.
12881      */
12882     readRecords : function(o){
12883         var sid = this.meta ? this.meta.id : null;
12884         var recordType = this.recordType, fields = recordType.prototype.fields;
12885         var records = [];
12886         var root = o;
12887             for(var i = 0; i < root.length; i++){
12888                     var n = root[i];
12889                 var values = {};
12890                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12891                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12892                 var f = fields.items[j];
12893                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12894                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12895                 v = f.convert(v);
12896                 values[f.name] = v;
12897             }
12898                 var record = new recordType(values, id);
12899                 record.json = n;
12900                 records[records.length] = record;
12901             }
12902             return {
12903                 records : records,
12904                 totalRecords : records.length
12905             };
12906     }
12907 });/*
12908  * - LGPL
12909  * * 
12910  */
12911
12912 /**
12913  * @class Roo.bootstrap.ComboBox
12914  * @extends Roo.bootstrap.TriggerField
12915  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12916  * @cfg {Boolean} append (true|false) default false
12917  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12918  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12919  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12920  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12921  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12922  * @cfg {Boolean} animate default true
12923  * @cfg {Boolean} emptyResultText only for touch device
12924  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12925  * @cfg {String} emptyTitle default ''
12926  * @constructor
12927  * Create a new ComboBox.
12928  * @param {Object} config Configuration options
12929  */
12930 Roo.bootstrap.ComboBox = function(config){
12931     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12932     this.addEvents({
12933         /**
12934          * @event expand
12935          * Fires when the dropdown list is expanded
12936         * @param {Roo.bootstrap.ComboBox} combo This combo box
12937         */
12938         'expand' : true,
12939         /**
12940          * @event collapse
12941          * Fires when the dropdown list is collapsed
12942         * @param {Roo.bootstrap.ComboBox} combo This combo box
12943         */
12944         'collapse' : true,
12945         /**
12946          * @event beforeselect
12947          * Fires before a list item is selected. Return false to cancel the selection.
12948         * @param {Roo.bootstrap.ComboBox} combo This combo box
12949         * @param {Roo.data.Record} record The data record returned from the underlying store
12950         * @param {Number} index The index of the selected item in the dropdown list
12951         */
12952         'beforeselect' : true,
12953         /**
12954          * @event select
12955          * Fires when a list item is selected
12956         * @param {Roo.bootstrap.ComboBox} combo This combo box
12957         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12958         * @param {Number} index The index of the selected item in the dropdown list
12959         */
12960         'select' : true,
12961         /**
12962          * @event beforequery
12963          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12964          * The event object passed has these properties:
12965         * @param {Roo.bootstrap.ComboBox} combo This combo box
12966         * @param {String} query The query
12967         * @param {Boolean} forceAll true to force "all" query
12968         * @param {Boolean} cancel true to cancel the query
12969         * @param {Object} e The query event object
12970         */
12971         'beforequery': true,
12972          /**
12973          * @event add
12974          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12975         * @param {Roo.bootstrap.ComboBox} combo This combo box
12976         */
12977         'add' : true,
12978         /**
12979          * @event edit
12980          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12981         * @param {Roo.bootstrap.ComboBox} combo This combo box
12982         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12983         */
12984         'edit' : true,
12985         /**
12986          * @event remove
12987          * Fires when the remove value from the combobox array
12988         * @param {Roo.bootstrap.ComboBox} combo This combo box
12989         */
12990         'remove' : true,
12991         /**
12992          * @event afterremove
12993          * Fires when the remove value from the combobox array
12994         * @param {Roo.bootstrap.ComboBox} combo This combo box
12995         */
12996         'afterremove' : true,
12997         /**
12998          * @event specialfilter
12999          * Fires when specialfilter
13000             * @param {Roo.bootstrap.ComboBox} combo This combo box
13001             */
13002         'specialfilter' : true,
13003         /**
13004          * @event tick
13005          * Fires when tick the element
13006             * @param {Roo.bootstrap.ComboBox} combo This combo box
13007             */
13008         'tick' : true,
13009         /**
13010          * @event touchviewdisplay
13011          * Fires when touch view require special display (default is using displayField)
13012             * @param {Roo.bootstrap.ComboBox} combo This combo box
13013             * @param {Object} cfg set html .
13014             */
13015         'touchviewdisplay' : true
13016         
13017     });
13018     
13019     this.item = [];
13020     this.tickItems = [];
13021     
13022     this.selectedIndex = -1;
13023     if(this.mode == 'local'){
13024         if(config.queryDelay === undefined){
13025             this.queryDelay = 10;
13026         }
13027         if(config.minChars === undefined){
13028             this.minChars = 0;
13029         }
13030     }
13031 };
13032
13033 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13034      
13035     /**
13036      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13037      * rendering into an Roo.Editor, defaults to false)
13038      */
13039     /**
13040      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13041      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13042      */
13043     /**
13044      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13045      */
13046     /**
13047      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13048      * the dropdown list (defaults to undefined, with no header element)
13049      */
13050
13051      /**
13052      * @cfg {String/Roo.Template} tpl The template to use to render the output
13053      */
13054      
13055      /**
13056      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13057      */
13058     listWidth: undefined,
13059     /**
13060      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13061      * mode = 'remote' or 'text' if mode = 'local')
13062      */
13063     displayField: undefined,
13064     
13065     /**
13066      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13067      * mode = 'remote' or 'value' if mode = 'local'). 
13068      * Note: use of a valueField requires the user make a selection
13069      * in order for a value to be mapped.
13070      */
13071     valueField: undefined,
13072     /**
13073      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13074      */
13075     modalTitle : '',
13076     
13077     /**
13078      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13079      * field's data value (defaults to the underlying DOM element's name)
13080      */
13081     hiddenName: undefined,
13082     /**
13083      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13084      */
13085     listClass: '',
13086     /**
13087      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13088      */
13089     selectedClass: 'active',
13090     
13091     /**
13092      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13093      */
13094     shadow:'sides',
13095     /**
13096      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13097      * anchor positions (defaults to 'tl-bl')
13098      */
13099     listAlign: 'tl-bl?',
13100     /**
13101      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13102      */
13103     maxHeight: 300,
13104     /**
13105      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13106      * query specified by the allQuery config option (defaults to 'query')
13107      */
13108     triggerAction: 'query',
13109     /**
13110      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13111      * (defaults to 4, does not apply if editable = false)
13112      */
13113     minChars : 4,
13114     /**
13115      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13116      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13117      */
13118     typeAhead: false,
13119     /**
13120      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13121      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13122      */
13123     queryDelay: 500,
13124     /**
13125      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13126      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13127      */
13128     pageSize: 0,
13129     /**
13130      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13131      * when editable = true (defaults to false)
13132      */
13133     selectOnFocus:false,
13134     /**
13135      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13136      */
13137     queryParam: 'query',
13138     /**
13139      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13140      * when mode = 'remote' (defaults to 'Loading...')
13141      */
13142     loadingText: 'Loading...',
13143     /**
13144      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13145      */
13146     resizable: false,
13147     /**
13148      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13149      */
13150     handleHeight : 8,
13151     /**
13152      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13153      * traditional select (defaults to true)
13154      */
13155     editable: true,
13156     /**
13157      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13158      */
13159     allQuery: '',
13160     /**
13161      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13162      */
13163     mode: 'remote',
13164     /**
13165      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13166      * listWidth has a higher value)
13167      */
13168     minListWidth : 70,
13169     /**
13170      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13171      * allow the user to set arbitrary text into the field (defaults to false)
13172      */
13173     forceSelection:false,
13174     /**
13175      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13176      * if typeAhead = true (defaults to 250)
13177      */
13178     typeAheadDelay : 250,
13179     /**
13180      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13181      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13182      */
13183     valueNotFoundText : undefined,
13184     /**
13185      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13186      */
13187     blockFocus : false,
13188     
13189     /**
13190      * @cfg {Boolean} disableClear Disable showing of clear button.
13191      */
13192     disableClear : false,
13193     /**
13194      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13195      */
13196     alwaysQuery : false,
13197     
13198     /**
13199      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13200      */
13201     multiple : false,
13202     
13203     /**
13204      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13205      */
13206     invalidClass : "has-warning",
13207     
13208     /**
13209      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13210      */
13211     validClass : "has-success",
13212     
13213     /**
13214      * @cfg {Boolean} specialFilter (true|false) special filter default false
13215      */
13216     specialFilter : false,
13217     
13218     /**
13219      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13220      */
13221     mobileTouchView : true,
13222     
13223     /**
13224      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13225      */
13226     useNativeIOS : false,
13227     
13228     /**
13229      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13230      */
13231     mobile_restrict_height : false,
13232     
13233     ios_options : false,
13234     
13235     //private
13236     addicon : false,
13237     editicon: false,
13238     
13239     page: 0,
13240     hasQuery: false,
13241     append: false,
13242     loadNext: false,
13243     autoFocus : true,
13244     tickable : false,
13245     btnPosition : 'right',
13246     triggerList : true,
13247     showToggleBtn : true,
13248     animate : true,
13249     emptyResultText: 'Empty',
13250     triggerText : 'Select',
13251     emptyTitle : '',
13252     
13253     // element that contains real text value.. (when hidden is used..)
13254     
13255     getAutoCreate : function()
13256     {   
13257         var cfg = false;
13258         //render
13259         /*
13260          * Render classic select for iso
13261          */
13262         
13263         if(Roo.isIOS && this.useNativeIOS){
13264             cfg = this.getAutoCreateNativeIOS();
13265             return cfg;
13266         }
13267         
13268         /*
13269          * Touch Devices
13270          */
13271         
13272         if(Roo.isTouch && this.mobileTouchView){
13273             cfg = this.getAutoCreateTouchView();
13274             return cfg;;
13275         }
13276         
13277         /*
13278          *  Normal ComboBox
13279          */
13280         if(!this.tickable){
13281             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13282             return cfg;
13283         }
13284         
13285         /*
13286          *  ComboBox with tickable selections
13287          */
13288              
13289         var align = this.labelAlign || this.parentLabelAlign();
13290         
13291         cfg = {
13292             cls : 'form-group roo-combobox-tickable' //input-group
13293         };
13294         
13295         var btn_text_select = '';
13296         var btn_text_done = '';
13297         var btn_text_cancel = '';
13298         
13299         if (this.btn_text_show) {
13300             btn_text_select = 'Select';
13301             btn_text_done = 'Done';
13302             btn_text_cancel = 'Cancel'; 
13303         }
13304         
13305         var buttons = {
13306             tag : 'div',
13307             cls : 'tickable-buttons',
13308             cn : [
13309                 {
13310                     tag : 'button',
13311                     type : 'button',
13312                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13313                     //html : this.triggerText
13314                     html: btn_text_select
13315                 },
13316                 {
13317                     tag : 'button',
13318                     type : 'button',
13319                     name : 'ok',
13320                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13321                     //html : 'Done'
13322                     html: btn_text_done
13323                 },
13324                 {
13325                     tag : 'button',
13326                     type : 'button',
13327                     name : 'cancel',
13328                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13329                     //html : 'Cancel'
13330                     html: btn_text_cancel
13331                 }
13332             ]
13333         };
13334         
13335         if(this.editable){
13336             buttons.cn.unshift({
13337                 tag: 'input',
13338                 cls: 'roo-select2-search-field-input'
13339             });
13340         }
13341         
13342         var _this = this;
13343         
13344         Roo.each(buttons.cn, function(c){
13345             if (_this.size) {
13346                 c.cls += ' btn-' + _this.size;
13347             }
13348
13349             if (_this.disabled) {
13350                 c.disabled = true;
13351             }
13352         });
13353         
13354         var box = {
13355             tag: 'div',
13356             cn: [
13357                 {
13358                     tag: 'input',
13359                     type : 'hidden',
13360                     cls: 'form-hidden-field'
13361                 },
13362                 {
13363                     tag: 'ul',
13364                     cls: 'roo-select2-choices',
13365                     cn:[
13366                         {
13367                             tag: 'li',
13368                             cls: 'roo-select2-search-field',
13369                             cn: [
13370                                 buttons
13371                             ]
13372                         }
13373                     ]
13374                 }
13375             ]
13376         };
13377         
13378         var combobox = {
13379             cls: 'roo-select2-container input-group roo-select2-container-multi',
13380             cn: [
13381                 
13382                 box
13383 //                {
13384 //                    tag: 'ul',
13385 //                    cls: 'typeahead typeahead-long dropdown-menu',
13386 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13387 //                }
13388             ]
13389         };
13390         
13391         if(this.hasFeedback && !this.allowBlank){
13392             
13393             var feedback = {
13394                 tag: 'span',
13395                 cls: 'glyphicon form-control-feedback'
13396             };
13397
13398             combobox.cn.push(feedback);
13399         }
13400         
13401         var indicator = {
13402             tag : 'i',
13403             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13404             tooltip : 'This field is required'
13405         };
13406         if (Roo.bootstrap.version == 4) {
13407             indicator = {
13408                 tag : 'i',
13409                 style : 'display:none'
13410             };
13411         }
13412         if (align ==='left' && this.fieldLabel.length) {
13413             
13414             cfg.cls += ' roo-form-group-label-left row';
13415             
13416             cfg.cn = [
13417                 indicator,
13418                 {
13419                     tag: 'label',
13420                     'for' :  id,
13421                     cls : 'control-label col-form-label',
13422                     html : this.fieldLabel
13423
13424                 },
13425                 {
13426                     cls : "", 
13427                     cn: [
13428                         combobox
13429                     ]
13430                 }
13431
13432             ];
13433             
13434             var labelCfg = cfg.cn[1];
13435             var contentCfg = cfg.cn[2];
13436             
13437
13438             if(this.indicatorpos == 'right'){
13439                 
13440                 cfg.cn = [
13441                     {
13442                         tag: 'label',
13443                         'for' :  id,
13444                         cls : 'control-label col-form-label',
13445                         cn : [
13446                             {
13447                                 tag : 'span',
13448                                 html : this.fieldLabel
13449                             },
13450                             indicator
13451                         ]
13452                     },
13453                     {
13454                         cls : "",
13455                         cn: [
13456                             combobox
13457                         ]
13458                     }
13459
13460                 ];
13461                 
13462                 
13463                 
13464                 labelCfg = cfg.cn[0];
13465                 contentCfg = cfg.cn[1];
13466             
13467             }
13468             
13469             if(this.labelWidth > 12){
13470                 labelCfg.style = "width: " + this.labelWidth + 'px';
13471             }
13472             
13473             if(this.labelWidth < 13 && this.labelmd == 0){
13474                 this.labelmd = this.labelWidth;
13475             }
13476             
13477             if(this.labellg > 0){
13478                 labelCfg.cls += ' col-lg-' + this.labellg;
13479                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13480             }
13481             
13482             if(this.labelmd > 0){
13483                 labelCfg.cls += ' col-md-' + this.labelmd;
13484                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13485             }
13486             
13487             if(this.labelsm > 0){
13488                 labelCfg.cls += ' col-sm-' + this.labelsm;
13489                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13490             }
13491             
13492             if(this.labelxs > 0){
13493                 labelCfg.cls += ' col-xs-' + this.labelxs;
13494                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13495             }
13496                 
13497                 
13498         } else if ( this.fieldLabel.length) {
13499 //                Roo.log(" label");
13500                  cfg.cn = [
13501                    indicator,
13502                     {
13503                         tag: 'label',
13504                         //cls : 'input-group-addon',
13505                         html : this.fieldLabel
13506                     },
13507                     combobox
13508                 ];
13509                 
13510                 if(this.indicatorpos == 'right'){
13511                     cfg.cn = [
13512                         {
13513                             tag: 'label',
13514                             //cls : 'input-group-addon',
13515                             html : this.fieldLabel
13516                         },
13517                         indicator,
13518                         combobox
13519                     ];
13520                     
13521                 }
13522
13523         } else {
13524             
13525 //                Roo.log(" no label && no align");
13526                 cfg = combobox
13527                      
13528                 
13529         }
13530          
13531         var settings=this;
13532         ['xs','sm','md','lg'].map(function(size){
13533             if (settings[size]) {
13534                 cfg.cls += ' col-' + size + '-' + settings[size];
13535             }
13536         });
13537         
13538         return cfg;
13539         
13540     },
13541     
13542     _initEventsCalled : false,
13543     
13544     // private
13545     initEvents: function()
13546     {   
13547         if (this._initEventsCalled) { // as we call render... prevent looping...
13548             return;
13549         }
13550         this._initEventsCalled = true;
13551         
13552         if (!this.store) {
13553             throw "can not find store for combo";
13554         }
13555         
13556         this.indicator = this.indicatorEl();
13557         
13558         this.store = Roo.factory(this.store, Roo.data);
13559         this.store.parent = this;
13560         
13561         // if we are building from html. then this element is so complex, that we can not really
13562         // use the rendered HTML.
13563         // so we have to trash and replace the previous code.
13564         if (Roo.XComponent.build_from_html) {
13565             // remove this element....
13566             var e = this.el.dom, k=0;
13567             while (e ) { e = e.previousSibling;  ++k;}
13568
13569             this.el.remove();
13570             
13571             this.el=false;
13572             this.rendered = false;
13573             
13574             this.render(this.parent().getChildContainer(true), k);
13575         }
13576         
13577         if(Roo.isIOS && this.useNativeIOS){
13578             this.initIOSView();
13579             return;
13580         }
13581         
13582         /*
13583          * Touch Devices
13584          */
13585         
13586         if(Roo.isTouch && this.mobileTouchView){
13587             this.initTouchView();
13588             return;
13589         }
13590         
13591         if(this.tickable){
13592             this.initTickableEvents();
13593             return;
13594         }
13595         
13596         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13597         
13598         if(this.hiddenName){
13599             
13600             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13601             
13602             this.hiddenField.dom.value =
13603                 this.hiddenValue !== undefined ? this.hiddenValue :
13604                 this.value !== undefined ? this.value : '';
13605
13606             // prevent input submission
13607             this.el.dom.removeAttribute('name');
13608             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13609              
13610              
13611         }
13612         //if(Roo.isGecko){
13613         //    this.el.dom.setAttribute('autocomplete', 'off');
13614         //}
13615         
13616         var cls = 'x-combo-list';
13617         
13618         //this.list = new Roo.Layer({
13619         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13620         //});
13621         
13622         var _this = this;
13623         
13624         (function(){
13625             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13626             _this.list.setWidth(lw);
13627         }).defer(100);
13628         
13629         this.list.on('mouseover', this.onViewOver, this);
13630         this.list.on('mousemove', this.onViewMove, this);
13631         this.list.on('scroll', this.onViewScroll, this);
13632         
13633         /*
13634         this.list.swallowEvent('mousewheel');
13635         this.assetHeight = 0;
13636
13637         if(this.title){
13638             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13639             this.assetHeight += this.header.getHeight();
13640         }
13641
13642         this.innerList = this.list.createChild({cls:cls+'-inner'});
13643         this.innerList.on('mouseover', this.onViewOver, this);
13644         this.innerList.on('mousemove', this.onViewMove, this);
13645         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13646         
13647         if(this.allowBlank && !this.pageSize && !this.disableClear){
13648             this.footer = this.list.createChild({cls:cls+'-ft'});
13649             this.pageTb = new Roo.Toolbar(this.footer);
13650            
13651         }
13652         if(this.pageSize){
13653             this.footer = this.list.createChild({cls:cls+'-ft'});
13654             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13655                     {pageSize: this.pageSize});
13656             
13657         }
13658         
13659         if (this.pageTb && this.allowBlank && !this.disableClear) {
13660             var _this = this;
13661             this.pageTb.add(new Roo.Toolbar.Fill(), {
13662                 cls: 'x-btn-icon x-btn-clear',
13663                 text: '&#160;',
13664                 handler: function()
13665                 {
13666                     _this.collapse();
13667                     _this.clearValue();
13668                     _this.onSelect(false, -1);
13669                 }
13670             });
13671         }
13672         if (this.footer) {
13673             this.assetHeight += this.footer.getHeight();
13674         }
13675         */
13676             
13677         if(!this.tpl){
13678             this.tpl = Roo.bootstrap.version == 4 ?
13679                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13680                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13681         }
13682
13683         this.view = new Roo.View(this.list, this.tpl, {
13684             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13685         });
13686         //this.view.wrapEl.setDisplayed(false);
13687         this.view.on('click', this.onViewClick, this);
13688         
13689         
13690         this.store.on('beforeload', this.onBeforeLoad, this);
13691         this.store.on('load', this.onLoad, this);
13692         this.store.on('loadexception', this.onLoadException, this);
13693         /*
13694         if(this.resizable){
13695             this.resizer = new Roo.Resizable(this.list,  {
13696                pinned:true, handles:'se'
13697             });
13698             this.resizer.on('resize', function(r, w, h){
13699                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13700                 this.listWidth = w;
13701                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13702                 this.restrictHeight();
13703             }, this);
13704             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13705         }
13706         */
13707         if(!this.editable){
13708             this.editable = true;
13709             this.setEditable(false);
13710         }
13711         
13712         /*
13713         
13714         if (typeof(this.events.add.listeners) != 'undefined') {
13715             
13716             this.addicon = this.wrap.createChild(
13717                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13718        
13719             this.addicon.on('click', function(e) {
13720                 this.fireEvent('add', this);
13721             }, this);
13722         }
13723         if (typeof(this.events.edit.listeners) != 'undefined') {
13724             
13725             this.editicon = this.wrap.createChild(
13726                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13727             if (this.addicon) {
13728                 this.editicon.setStyle('margin-left', '40px');
13729             }
13730             this.editicon.on('click', function(e) {
13731                 
13732                 // we fire even  if inothing is selected..
13733                 this.fireEvent('edit', this, this.lastData );
13734                 
13735             }, this);
13736         }
13737         */
13738         
13739         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13740             "up" : function(e){
13741                 this.inKeyMode = true;
13742                 this.selectPrev();
13743             },
13744
13745             "down" : function(e){
13746                 if(!this.isExpanded()){
13747                     this.onTriggerClick();
13748                 }else{
13749                     this.inKeyMode = true;
13750                     this.selectNext();
13751                 }
13752             },
13753
13754             "enter" : function(e){
13755 //                this.onViewClick();
13756                 //return true;
13757                 this.collapse();
13758                 
13759                 if(this.fireEvent("specialkey", this, e)){
13760                     this.onViewClick(false);
13761                 }
13762                 
13763                 return true;
13764             },
13765
13766             "esc" : function(e){
13767                 this.collapse();
13768             },
13769
13770             "tab" : function(e){
13771                 this.collapse();
13772                 
13773                 if(this.fireEvent("specialkey", this, e)){
13774                     this.onViewClick(false);
13775                 }
13776                 
13777                 return true;
13778             },
13779
13780             scope : this,
13781
13782             doRelay : function(foo, bar, hname){
13783                 if(hname == 'down' || this.scope.isExpanded()){
13784                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13785                 }
13786                 return true;
13787             },
13788
13789             forceKeyDown: true
13790         });
13791         
13792         
13793         this.queryDelay = Math.max(this.queryDelay || 10,
13794                 this.mode == 'local' ? 10 : 250);
13795         
13796         
13797         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13798         
13799         if(this.typeAhead){
13800             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13801         }
13802         if(this.editable !== false){
13803             this.inputEl().on("keyup", this.onKeyUp, this);
13804         }
13805         if(this.forceSelection){
13806             this.inputEl().on('blur', this.doForce, this);
13807         }
13808         
13809         if(this.multiple){
13810             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13811             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13812         }
13813     },
13814     
13815     initTickableEvents: function()
13816     {   
13817         this.createList();
13818         
13819         if(this.hiddenName){
13820             
13821             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13822             
13823             this.hiddenField.dom.value =
13824                 this.hiddenValue !== undefined ? this.hiddenValue :
13825                 this.value !== undefined ? this.value : '';
13826
13827             // prevent input submission
13828             this.el.dom.removeAttribute('name');
13829             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13830              
13831              
13832         }
13833         
13834 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13835         
13836         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13837         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13838         if(this.triggerList){
13839             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13840         }
13841          
13842         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13843         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13844         
13845         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13846         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13847         
13848         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13849         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13850         
13851         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13852         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13853         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13854         
13855         this.okBtn.hide();
13856         this.cancelBtn.hide();
13857         
13858         var _this = this;
13859         
13860         (function(){
13861             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13862             _this.list.setWidth(lw);
13863         }).defer(100);
13864         
13865         this.list.on('mouseover', this.onViewOver, this);
13866         this.list.on('mousemove', this.onViewMove, this);
13867         
13868         this.list.on('scroll', this.onViewScroll, this);
13869         
13870         if(!this.tpl){
13871             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13872                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13873         }
13874
13875         this.view = new Roo.View(this.list, this.tpl, {
13876             singleSelect:true,
13877             tickable:true,
13878             parent:this,
13879             store: this.store,
13880             selectedClass: this.selectedClass
13881         });
13882         
13883         //this.view.wrapEl.setDisplayed(false);
13884         this.view.on('click', this.onViewClick, this);
13885         
13886         
13887         
13888         this.store.on('beforeload', this.onBeforeLoad, this);
13889         this.store.on('load', this.onLoad, this);
13890         this.store.on('loadexception', this.onLoadException, this);
13891         
13892         if(this.editable){
13893             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13894                 "up" : function(e){
13895                     this.inKeyMode = true;
13896                     this.selectPrev();
13897                 },
13898
13899                 "down" : function(e){
13900                     this.inKeyMode = true;
13901                     this.selectNext();
13902                 },
13903
13904                 "enter" : function(e){
13905                     if(this.fireEvent("specialkey", this, e)){
13906                         this.onViewClick(false);
13907                     }
13908                     
13909                     return true;
13910                 },
13911
13912                 "esc" : function(e){
13913                     this.onTickableFooterButtonClick(e, false, false);
13914                 },
13915
13916                 "tab" : function(e){
13917                     this.fireEvent("specialkey", this, e);
13918                     
13919                     this.onTickableFooterButtonClick(e, false, false);
13920                     
13921                     return true;
13922                 },
13923
13924                 scope : this,
13925
13926                 doRelay : function(e, fn, key){
13927                     if(this.scope.isExpanded()){
13928                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13929                     }
13930                     return true;
13931                 },
13932
13933                 forceKeyDown: true
13934             });
13935         }
13936         
13937         this.queryDelay = Math.max(this.queryDelay || 10,
13938                 this.mode == 'local' ? 10 : 250);
13939         
13940         
13941         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13942         
13943         if(this.typeAhead){
13944             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13945         }
13946         
13947         if(this.editable !== false){
13948             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13949         }
13950         
13951         this.indicator = this.indicatorEl();
13952         
13953         if(this.indicator){
13954             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13955             this.indicator.hide();
13956         }
13957         
13958     },
13959
13960     onDestroy : function(){
13961         if(this.view){
13962             this.view.setStore(null);
13963             this.view.el.removeAllListeners();
13964             this.view.el.remove();
13965             this.view.purgeListeners();
13966         }
13967         if(this.list){
13968             this.list.dom.innerHTML  = '';
13969         }
13970         
13971         if(this.store){
13972             this.store.un('beforeload', this.onBeforeLoad, this);
13973             this.store.un('load', this.onLoad, this);
13974             this.store.un('loadexception', this.onLoadException, this);
13975         }
13976         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13977     },
13978
13979     // private
13980     fireKey : function(e){
13981         if(e.isNavKeyPress() && !this.list.isVisible()){
13982             this.fireEvent("specialkey", this, e);
13983         }
13984     },
13985
13986     // private
13987     onResize: function(w, h){
13988 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13989 //        
13990 //        if(typeof w != 'number'){
13991 //            // we do not handle it!?!?
13992 //            return;
13993 //        }
13994 //        var tw = this.trigger.getWidth();
13995 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13996 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13997 //        var x = w - tw;
13998 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13999 //            
14000 //        //this.trigger.setStyle('left', x+'px');
14001 //        
14002 //        if(this.list && this.listWidth === undefined){
14003 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14004 //            this.list.setWidth(lw);
14005 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14006 //        }
14007         
14008     
14009         
14010     },
14011
14012     /**
14013      * Allow or prevent the user from directly editing the field text.  If false is passed,
14014      * the user will only be able to select from the items defined in the dropdown list.  This method
14015      * is the runtime equivalent of setting the 'editable' config option at config time.
14016      * @param {Boolean} value True to allow the user to directly edit the field text
14017      */
14018     setEditable : function(value){
14019         if(value == this.editable){
14020             return;
14021         }
14022         this.editable = value;
14023         if(!value){
14024             this.inputEl().dom.setAttribute('readOnly', true);
14025             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14026             this.inputEl().addClass('x-combo-noedit');
14027         }else{
14028             this.inputEl().dom.setAttribute('readOnly', false);
14029             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14030             this.inputEl().removeClass('x-combo-noedit');
14031         }
14032     },
14033
14034     // private
14035     
14036     onBeforeLoad : function(combo,opts){
14037         if(!this.hasFocus){
14038             return;
14039         }
14040          if (!opts.add) {
14041             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14042          }
14043         this.restrictHeight();
14044         this.selectedIndex = -1;
14045     },
14046
14047     // private
14048     onLoad : function(){
14049         
14050         this.hasQuery = false;
14051         
14052         if(!this.hasFocus){
14053             return;
14054         }
14055         
14056         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14057             this.loading.hide();
14058         }
14059         
14060         if(this.store.getCount() > 0){
14061             
14062             this.expand();
14063             this.restrictHeight();
14064             if(this.lastQuery == this.allQuery){
14065                 if(this.editable && !this.tickable){
14066                     this.inputEl().dom.select();
14067                 }
14068                 
14069                 if(
14070                     !this.selectByValue(this.value, true) &&
14071                     this.autoFocus && 
14072                     (
14073                         !this.store.lastOptions ||
14074                         typeof(this.store.lastOptions.add) == 'undefined' || 
14075                         this.store.lastOptions.add != true
14076                     )
14077                 ){
14078                     this.select(0, true);
14079                 }
14080             }else{
14081                 if(this.autoFocus){
14082                     this.selectNext();
14083                 }
14084                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14085                     this.taTask.delay(this.typeAheadDelay);
14086                 }
14087             }
14088         }else{
14089             this.onEmptyResults();
14090         }
14091         
14092         //this.el.focus();
14093     },
14094     // private
14095     onLoadException : function()
14096     {
14097         this.hasQuery = false;
14098         
14099         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14100             this.loading.hide();
14101         }
14102         
14103         if(this.tickable && this.editable){
14104             return;
14105         }
14106         
14107         this.collapse();
14108         // only causes errors at present
14109         //Roo.log(this.store.reader.jsonData);
14110         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14111             // fixme
14112             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14113         //}
14114         
14115         
14116     },
14117     // private
14118     onTypeAhead : function(){
14119         if(this.store.getCount() > 0){
14120             var r = this.store.getAt(0);
14121             var newValue = r.data[this.displayField];
14122             var len = newValue.length;
14123             var selStart = this.getRawValue().length;
14124             
14125             if(selStart != len){
14126                 this.setRawValue(newValue);
14127                 this.selectText(selStart, newValue.length);
14128             }
14129         }
14130     },
14131
14132     // private
14133     onSelect : function(record, index){
14134         
14135         if(this.fireEvent('beforeselect', this, record, index) !== false){
14136         
14137             this.setFromData(index > -1 ? record.data : false);
14138             
14139             this.collapse();
14140             this.fireEvent('select', this, record, index);
14141         }
14142     },
14143
14144     /**
14145      * Returns the currently selected field value or empty string if no value is set.
14146      * @return {String} value The selected value
14147      */
14148     getValue : function()
14149     {
14150         if(Roo.isIOS && this.useNativeIOS){
14151             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14152         }
14153         
14154         if(this.multiple){
14155             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14156         }
14157         
14158         if(this.valueField){
14159             return typeof this.value != 'undefined' ? this.value : '';
14160         }else{
14161             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14162         }
14163     },
14164     
14165     getRawValue : function()
14166     {
14167         if(Roo.isIOS && this.useNativeIOS){
14168             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14169         }
14170         
14171         var v = this.inputEl().getValue();
14172         
14173         return v;
14174     },
14175
14176     /**
14177      * Clears any text/value currently set in the field
14178      */
14179     clearValue : function(){
14180         
14181         if(this.hiddenField){
14182             this.hiddenField.dom.value = '';
14183         }
14184         this.value = '';
14185         this.setRawValue('');
14186         this.lastSelectionText = '';
14187         this.lastData = false;
14188         
14189         var close = this.closeTriggerEl();
14190         
14191         if(close){
14192             close.hide();
14193         }
14194         
14195         this.validate();
14196         
14197     },
14198
14199     /**
14200      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14201      * will be displayed in the field.  If the value does not match the data value of an existing item,
14202      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14203      * Otherwise the field will be blank (although the value will still be set).
14204      * @param {String} value The value to match
14205      */
14206     setValue : function(v)
14207     {
14208         if(Roo.isIOS && this.useNativeIOS){
14209             this.setIOSValue(v);
14210             return;
14211         }
14212         
14213         if(this.multiple){
14214             this.syncValue();
14215             return;
14216         }
14217         
14218         var text = v;
14219         if(this.valueField){
14220             var r = this.findRecord(this.valueField, v);
14221             if(r){
14222                 text = r.data[this.displayField];
14223             }else if(this.valueNotFoundText !== undefined){
14224                 text = this.valueNotFoundText;
14225             }
14226         }
14227         this.lastSelectionText = text;
14228         if(this.hiddenField){
14229             this.hiddenField.dom.value = v;
14230         }
14231         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14232         this.value = v;
14233         
14234         var close = this.closeTriggerEl();
14235         
14236         if(close){
14237             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14238         }
14239         
14240         this.validate();
14241     },
14242     /**
14243      * @property {Object} the last set data for the element
14244      */
14245     
14246     lastData : false,
14247     /**
14248      * Sets the value of the field based on a object which is related to the record format for the store.
14249      * @param {Object} value the value to set as. or false on reset?
14250      */
14251     setFromData : function(o){
14252         
14253         if(this.multiple){
14254             this.addItem(o);
14255             return;
14256         }
14257             
14258         var dv = ''; // display value
14259         var vv = ''; // value value..
14260         this.lastData = o;
14261         if (this.displayField) {
14262             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14263         } else {
14264             // this is an error condition!!!
14265             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14266         }
14267         
14268         if(this.valueField){
14269             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14270         }
14271         
14272         var close = this.closeTriggerEl();
14273         
14274         if(close){
14275             if(dv.length || vv * 1 > 0){
14276                 close.show() ;
14277                 this.blockFocus=true;
14278             } else {
14279                 close.hide();
14280             }             
14281         }
14282         
14283         if(this.hiddenField){
14284             this.hiddenField.dom.value = vv;
14285             
14286             this.lastSelectionText = dv;
14287             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14288             this.value = vv;
14289             return;
14290         }
14291         // no hidden field.. - we store the value in 'value', but still display
14292         // display field!!!!
14293         this.lastSelectionText = dv;
14294         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14295         this.value = vv;
14296         
14297         
14298         
14299     },
14300     // private
14301     reset : function(){
14302         // overridden so that last data is reset..
14303         
14304         if(this.multiple){
14305             this.clearItem();
14306             return;
14307         }
14308         
14309         this.setValue(this.originalValue);
14310         //this.clearInvalid();
14311         this.lastData = false;
14312         if (this.view) {
14313             this.view.clearSelections();
14314         }
14315         
14316         this.validate();
14317     },
14318     // private
14319     findRecord : function(prop, value){
14320         var record;
14321         if(this.store.getCount() > 0){
14322             this.store.each(function(r){
14323                 if(r.data[prop] == value){
14324                     record = r;
14325                     return false;
14326                 }
14327                 return true;
14328             });
14329         }
14330         return record;
14331     },
14332     
14333     getName: function()
14334     {
14335         // returns hidden if it's set..
14336         if (!this.rendered) {return ''};
14337         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14338         
14339     },
14340     // private
14341     onViewMove : function(e, t){
14342         this.inKeyMode = false;
14343     },
14344
14345     // private
14346     onViewOver : function(e, t){
14347         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14348             return;
14349         }
14350         var item = this.view.findItemFromChild(t);
14351         
14352         if(item){
14353             var index = this.view.indexOf(item);
14354             this.select(index, false);
14355         }
14356     },
14357
14358     // private
14359     onViewClick : function(view, doFocus, el, e)
14360     {
14361         var index = this.view.getSelectedIndexes()[0];
14362         
14363         var r = this.store.getAt(index);
14364         
14365         if(this.tickable){
14366             
14367             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14368                 return;
14369             }
14370             
14371             var rm = false;
14372             var _this = this;
14373             
14374             Roo.each(this.tickItems, function(v,k){
14375                 
14376                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14377                     Roo.log(v);
14378                     _this.tickItems.splice(k, 1);
14379                     
14380                     if(typeof(e) == 'undefined' && view == false){
14381                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14382                     }
14383                     
14384                     rm = true;
14385                     return;
14386                 }
14387             });
14388             
14389             if(rm){
14390                 return;
14391             }
14392             
14393             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14394                 this.tickItems.push(r.data);
14395             }
14396             
14397             if(typeof(e) == 'undefined' && view == false){
14398                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14399             }
14400                     
14401             return;
14402         }
14403         
14404         if(r){
14405             this.onSelect(r, index);
14406         }
14407         if(doFocus !== false && !this.blockFocus){
14408             this.inputEl().focus();
14409         }
14410     },
14411
14412     // private
14413     restrictHeight : function(){
14414         //this.innerList.dom.style.height = '';
14415         //var inner = this.innerList.dom;
14416         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14417         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14418         //this.list.beginUpdate();
14419         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14420         this.list.alignTo(this.inputEl(), this.listAlign);
14421         this.list.alignTo(this.inputEl(), this.listAlign);
14422         //this.list.endUpdate();
14423     },
14424
14425     // private
14426     onEmptyResults : function(){
14427         
14428         if(this.tickable && this.editable){
14429             this.hasFocus = false;
14430             this.restrictHeight();
14431             return;
14432         }
14433         
14434         this.collapse();
14435     },
14436
14437     /**
14438      * Returns true if the dropdown list is expanded, else false.
14439      */
14440     isExpanded : function(){
14441         return this.list.isVisible();
14442     },
14443
14444     /**
14445      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14446      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14447      * @param {String} value The data value of the item to select
14448      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14449      * selected item if it is not currently in view (defaults to true)
14450      * @return {Boolean} True if the value matched an item in the list, else false
14451      */
14452     selectByValue : function(v, scrollIntoView){
14453         if(v !== undefined && v !== null){
14454             var r = this.findRecord(this.valueField || this.displayField, v);
14455             if(r){
14456                 this.select(this.store.indexOf(r), scrollIntoView);
14457                 return true;
14458             }
14459         }
14460         return false;
14461     },
14462
14463     /**
14464      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14465      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14466      * @param {Number} index The zero-based index of the list item to select
14467      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14468      * selected item if it is not currently in view (defaults to true)
14469      */
14470     select : function(index, scrollIntoView){
14471         this.selectedIndex = index;
14472         this.view.select(index);
14473         if(scrollIntoView !== false){
14474             var el = this.view.getNode(index);
14475             /*
14476              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14477              */
14478             if(el){
14479                 this.list.scrollChildIntoView(el, false);
14480             }
14481         }
14482     },
14483
14484     // private
14485     selectNext : function(){
14486         var ct = this.store.getCount();
14487         if(ct > 0){
14488             if(this.selectedIndex == -1){
14489                 this.select(0);
14490             }else if(this.selectedIndex < ct-1){
14491                 this.select(this.selectedIndex+1);
14492             }
14493         }
14494     },
14495
14496     // private
14497     selectPrev : function(){
14498         var ct = this.store.getCount();
14499         if(ct > 0){
14500             if(this.selectedIndex == -1){
14501                 this.select(0);
14502             }else if(this.selectedIndex != 0){
14503                 this.select(this.selectedIndex-1);
14504             }
14505         }
14506     },
14507
14508     // private
14509     onKeyUp : function(e){
14510         if(this.editable !== false && !e.isSpecialKey()){
14511             this.lastKey = e.getKey();
14512             this.dqTask.delay(this.queryDelay);
14513         }
14514     },
14515
14516     // private
14517     validateBlur : function(){
14518         return !this.list || !this.list.isVisible();   
14519     },
14520
14521     // private
14522     initQuery : function(){
14523         
14524         var v = this.getRawValue();
14525         
14526         if(this.tickable && this.editable){
14527             v = this.tickableInputEl().getValue();
14528         }
14529         
14530         this.doQuery(v);
14531     },
14532
14533     // private
14534     doForce : function(){
14535         if(this.inputEl().dom.value.length > 0){
14536             this.inputEl().dom.value =
14537                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14538              
14539         }
14540     },
14541
14542     /**
14543      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14544      * query allowing the query action to be canceled if needed.
14545      * @param {String} query The SQL query to execute
14546      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14547      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14548      * saved in the current store (defaults to false)
14549      */
14550     doQuery : function(q, forceAll){
14551         
14552         if(q === undefined || q === null){
14553             q = '';
14554         }
14555         var qe = {
14556             query: q,
14557             forceAll: forceAll,
14558             combo: this,
14559             cancel:false
14560         };
14561         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14562             return false;
14563         }
14564         q = qe.query;
14565         
14566         forceAll = qe.forceAll;
14567         if(forceAll === true || (q.length >= this.minChars)){
14568             
14569             this.hasQuery = true;
14570             
14571             if(this.lastQuery != q || this.alwaysQuery){
14572                 this.lastQuery = q;
14573                 if(this.mode == 'local'){
14574                     this.selectedIndex = -1;
14575                     if(forceAll){
14576                         this.store.clearFilter();
14577                     }else{
14578                         
14579                         if(this.specialFilter){
14580                             this.fireEvent('specialfilter', this);
14581                             this.onLoad();
14582                             return;
14583                         }
14584                         
14585                         this.store.filter(this.displayField, q);
14586                     }
14587                     
14588                     this.store.fireEvent("datachanged", this.store);
14589                     
14590                     this.onLoad();
14591                     
14592                     
14593                 }else{
14594                     
14595                     this.store.baseParams[this.queryParam] = q;
14596                     
14597                     var options = {params : this.getParams(q)};
14598                     
14599                     if(this.loadNext){
14600                         options.add = true;
14601                         options.params.start = this.page * this.pageSize;
14602                     }
14603                     
14604                     this.store.load(options);
14605                     
14606                     /*
14607                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14608                      *  we should expand the list on onLoad
14609                      *  so command out it
14610                      */
14611 //                    this.expand();
14612                 }
14613             }else{
14614                 this.selectedIndex = -1;
14615                 this.onLoad();   
14616             }
14617         }
14618         
14619         this.loadNext = false;
14620     },
14621     
14622     // private
14623     getParams : function(q){
14624         var p = {};
14625         //p[this.queryParam] = q;
14626         
14627         if(this.pageSize){
14628             p.start = 0;
14629             p.limit = this.pageSize;
14630         }
14631         return p;
14632     },
14633
14634     /**
14635      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14636      */
14637     collapse : function(){
14638         if(!this.isExpanded()){
14639             return;
14640         }
14641         
14642         this.list.hide();
14643         
14644         this.hasFocus = false;
14645         
14646         if(this.tickable){
14647             this.okBtn.hide();
14648             this.cancelBtn.hide();
14649             this.trigger.show();
14650             
14651             if(this.editable){
14652                 this.tickableInputEl().dom.value = '';
14653                 this.tickableInputEl().blur();
14654             }
14655             
14656         }
14657         
14658         Roo.get(document).un('mousedown', this.collapseIf, this);
14659         Roo.get(document).un('mousewheel', this.collapseIf, this);
14660         if (!this.editable) {
14661             Roo.get(document).un('keydown', this.listKeyPress, this);
14662         }
14663         this.fireEvent('collapse', this);
14664         
14665         this.validate();
14666     },
14667
14668     // private
14669     collapseIf : function(e){
14670         var in_combo  = e.within(this.el);
14671         var in_list =  e.within(this.list);
14672         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14673         
14674         if (in_combo || in_list || is_list) {
14675             //e.stopPropagation();
14676             return;
14677         }
14678         
14679         if(this.tickable){
14680             this.onTickableFooterButtonClick(e, false, false);
14681         }
14682
14683         this.collapse();
14684         
14685     },
14686
14687     /**
14688      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14689      */
14690     expand : function(){
14691        
14692         if(this.isExpanded() || !this.hasFocus){
14693             return;
14694         }
14695         
14696         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14697         this.list.setWidth(lw);
14698         
14699         Roo.log('expand');
14700         
14701         this.list.show();
14702         
14703         this.restrictHeight();
14704         
14705         if(this.tickable){
14706             
14707             this.tickItems = Roo.apply([], this.item);
14708             
14709             this.okBtn.show();
14710             this.cancelBtn.show();
14711             this.trigger.hide();
14712             
14713             if(this.editable){
14714                 this.tickableInputEl().focus();
14715             }
14716             
14717         }
14718         
14719         Roo.get(document).on('mousedown', this.collapseIf, this);
14720         Roo.get(document).on('mousewheel', this.collapseIf, this);
14721         if (!this.editable) {
14722             Roo.get(document).on('keydown', this.listKeyPress, this);
14723         }
14724         
14725         this.fireEvent('expand', this);
14726     },
14727
14728     // private
14729     // Implements the default empty TriggerField.onTriggerClick function
14730     onTriggerClick : function(e)
14731     {
14732         Roo.log('trigger click');
14733         
14734         if(this.disabled || !this.triggerList){
14735             return;
14736         }
14737         
14738         this.page = 0;
14739         this.loadNext = false;
14740         
14741         if(this.isExpanded()){
14742             this.collapse();
14743             if (!this.blockFocus) {
14744                 this.inputEl().focus();
14745             }
14746             
14747         }else {
14748             this.hasFocus = true;
14749             if(this.triggerAction == 'all') {
14750                 this.doQuery(this.allQuery, true);
14751             } else {
14752                 this.doQuery(this.getRawValue());
14753             }
14754             if (!this.blockFocus) {
14755                 this.inputEl().focus();
14756             }
14757         }
14758     },
14759     
14760     onTickableTriggerClick : function(e)
14761     {
14762         if(this.disabled){
14763             return;
14764         }
14765         
14766         this.page = 0;
14767         this.loadNext = false;
14768         this.hasFocus = true;
14769         
14770         if(this.triggerAction == 'all') {
14771             this.doQuery(this.allQuery, true);
14772         } else {
14773             this.doQuery(this.getRawValue());
14774         }
14775     },
14776     
14777     onSearchFieldClick : function(e)
14778     {
14779         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14780             this.onTickableFooterButtonClick(e, false, false);
14781             return;
14782         }
14783         
14784         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14785             return;
14786         }
14787         
14788         this.page = 0;
14789         this.loadNext = false;
14790         this.hasFocus = true;
14791         
14792         if(this.triggerAction == 'all') {
14793             this.doQuery(this.allQuery, true);
14794         } else {
14795             this.doQuery(this.getRawValue());
14796         }
14797     },
14798     
14799     listKeyPress : function(e)
14800     {
14801         //Roo.log('listkeypress');
14802         // scroll to first matching element based on key pres..
14803         if (e.isSpecialKey()) {
14804             return false;
14805         }
14806         var k = String.fromCharCode(e.getKey()).toUpperCase();
14807         //Roo.log(k);
14808         var match  = false;
14809         var csel = this.view.getSelectedNodes();
14810         var cselitem = false;
14811         if (csel.length) {
14812             var ix = this.view.indexOf(csel[0]);
14813             cselitem  = this.store.getAt(ix);
14814             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14815                 cselitem = false;
14816             }
14817             
14818         }
14819         
14820         this.store.each(function(v) { 
14821             if (cselitem) {
14822                 // start at existing selection.
14823                 if (cselitem.id == v.id) {
14824                     cselitem = false;
14825                 }
14826                 return true;
14827             }
14828                 
14829             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14830                 match = this.store.indexOf(v);
14831                 return false;
14832             }
14833             return true;
14834         }, this);
14835         
14836         if (match === false) {
14837             return true; // no more action?
14838         }
14839         // scroll to?
14840         this.view.select(match);
14841         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14842         sn.scrollIntoView(sn.dom.parentNode, false);
14843     },
14844     
14845     onViewScroll : function(e, t){
14846         
14847         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){
14848             return;
14849         }
14850         
14851         this.hasQuery = true;
14852         
14853         this.loading = this.list.select('.loading', true).first();
14854         
14855         if(this.loading === null){
14856             this.list.createChild({
14857                 tag: 'div',
14858                 cls: 'loading roo-select2-more-results roo-select2-active',
14859                 html: 'Loading more results...'
14860             });
14861             
14862             this.loading = this.list.select('.loading', true).first();
14863             
14864             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14865             
14866             this.loading.hide();
14867         }
14868         
14869         this.loading.show();
14870         
14871         var _combo = this;
14872         
14873         this.page++;
14874         this.loadNext = true;
14875         
14876         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14877         
14878         return;
14879     },
14880     
14881     addItem : function(o)
14882     {   
14883         var dv = ''; // display value
14884         
14885         if (this.displayField) {
14886             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14887         } else {
14888             // this is an error condition!!!
14889             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14890         }
14891         
14892         if(!dv.length){
14893             return;
14894         }
14895         
14896         var choice = this.choices.createChild({
14897             tag: 'li',
14898             cls: 'roo-select2-search-choice',
14899             cn: [
14900                 {
14901                     tag: 'div',
14902                     html: dv
14903                 },
14904                 {
14905                     tag: 'a',
14906                     href: '#',
14907                     cls: 'roo-select2-search-choice-close fa fa-times',
14908                     tabindex: '-1'
14909                 }
14910             ]
14911             
14912         }, this.searchField);
14913         
14914         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14915         
14916         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14917         
14918         this.item.push(o);
14919         
14920         this.lastData = o;
14921         
14922         this.syncValue();
14923         
14924         this.inputEl().dom.value = '';
14925         
14926         this.validate();
14927     },
14928     
14929     onRemoveItem : function(e, _self, o)
14930     {
14931         e.preventDefault();
14932         
14933         this.lastItem = Roo.apply([], this.item);
14934         
14935         var index = this.item.indexOf(o.data) * 1;
14936         
14937         if( index < 0){
14938             Roo.log('not this item?!');
14939             return;
14940         }
14941         
14942         this.item.splice(index, 1);
14943         o.item.remove();
14944         
14945         this.syncValue();
14946         
14947         this.fireEvent('remove', this, e);
14948         
14949         this.validate();
14950         
14951     },
14952     
14953     syncValue : function()
14954     {
14955         if(!this.item.length){
14956             this.clearValue();
14957             return;
14958         }
14959             
14960         var value = [];
14961         var _this = this;
14962         Roo.each(this.item, function(i){
14963             if(_this.valueField){
14964                 value.push(i[_this.valueField]);
14965                 return;
14966             }
14967
14968             value.push(i);
14969         });
14970
14971         this.value = value.join(',');
14972
14973         if(this.hiddenField){
14974             this.hiddenField.dom.value = this.value;
14975         }
14976         
14977         this.store.fireEvent("datachanged", this.store);
14978         
14979         this.validate();
14980     },
14981     
14982     clearItem : function()
14983     {
14984         if(!this.multiple){
14985             return;
14986         }
14987         
14988         this.item = [];
14989         
14990         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14991            c.remove();
14992         });
14993         
14994         this.syncValue();
14995         
14996         this.validate();
14997         
14998         if(this.tickable && !Roo.isTouch){
14999             this.view.refresh();
15000         }
15001     },
15002     
15003     inputEl: function ()
15004     {
15005         if(Roo.isIOS && this.useNativeIOS){
15006             return this.el.select('select.roo-ios-select', true).first();
15007         }
15008         
15009         if(Roo.isTouch && this.mobileTouchView){
15010             return this.el.select('input.form-control',true).first();
15011         }
15012         
15013         if(this.tickable){
15014             return this.searchField;
15015         }
15016         
15017         return this.el.select('input.form-control',true).first();
15018     },
15019     
15020     onTickableFooterButtonClick : function(e, btn, el)
15021     {
15022         e.preventDefault();
15023         
15024         this.lastItem = Roo.apply([], this.item);
15025         
15026         if(btn && btn.name == 'cancel'){
15027             this.tickItems = Roo.apply([], this.item);
15028             this.collapse();
15029             return;
15030         }
15031         
15032         this.clearItem();
15033         
15034         var _this = this;
15035         
15036         Roo.each(this.tickItems, function(o){
15037             _this.addItem(o);
15038         });
15039         
15040         this.collapse();
15041         
15042     },
15043     
15044     validate : function()
15045     {
15046         if(this.getVisibilityEl().hasClass('hidden')){
15047             return true;
15048         }
15049         
15050         var v = this.getRawValue();
15051         
15052         if(this.multiple){
15053             v = this.getValue();
15054         }
15055         
15056         if(this.disabled || this.allowBlank || v.length){
15057             this.markValid();
15058             return true;
15059         }
15060         
15061         this.markInvalid();
15062         return false;
15063     },
15064     
15065     tickableInputEl : function()
15066     {
15067         if(!this.tickable || !this.editable){
15068             return this.inputEl();
15069         }
15070         
15071         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15072     },
15073     
15074     
15075     getAutoCreateTouchView : function()
15076     {
15077         var id = Roo.id();
15078         
15079         var cfg = {
15080             cls: 'form-group' //input-group
15081         };
15082         
15083         var input =  {
15084             tag: 'input',
15085             id : id,
15086             type : this.inputType,
15087             cls : 'form-control x-combo-noedit',
15088             autocomplete: 'new-password',
15089             placeholder : this.placeholder || '',
15090             readonly : true
15091         };
15092         
15093         if (this.name) {
15094             input.name = this.name;
15095         }
15096         
15097         if (this.size) {
15098             input.cls += ' input-' + this.size;
15099         }
15100         
15101         if (this.disabled) {
15102             input.disabled = true;
15103         }
15104         
15105         var inputblock = {
15106             cls : '',
15107             cn : [
15108                 input
15109             ]
15110         };
15111         
15112         if(this.before){
15113             inputblock.cls += ' input-group';
15114             
15115             inputblock.cn.unshift({
15116                 tag :'span',
15117                 cls : 'input-group-addon input-group-prepend input-group-text',
15118                 html : this.before
15119             });
15120         }
15121         
15122         if(this.removable && !this.multiple){
15123             inputblock.cls += ' roo-removable';
15124             
15125             inputblock.cn.push({
15126                 tag: 'button',
15127                 html : 'x',
15128                 cls : 'roo-combo-removable-btn close'
15129             });
15130         }
15131
15132         if(this.hasFeedback && !this.allowBlank){
15133             
15134             inputblock.cls += ' has-feedback';
15135             
15136             inputblock.cn.push({
15137                 tag: 'span',
15138                 cls: 'glyphicon form-control-feedback'
15139             });
15140             
15141         }
15142         
15143         if (this.after) {
15144             
15145             inputblock.cls += (this.before) ? '' : ' input-group';
15146             
15147             inputblock.cn.push({
15148                 tag :'span',
15149                 cls : 'input-group-addon input-group-append input-group-text',
15150                 html : this.after
15151             });
15152         }
15153
15154         
15155         var ibwrap = inputblock;
15156         
15157         if(this.multiple){
15158             ibwrap = {
15159                 tag: 'ul',
15160                 cls: 'roo-select2-choices',
15161                 cn:[
15162                     {
15163                         tag: 'li',
15164                         cls: 'roo-select2-search-field',
15165                         cn: [
15166
15167                             inputblock
15168                         ]
15169                     }
15170                 ]
15171             };
15172         
15173             
15174         }
15175         
15176         var combobox = {
15177             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15178             cn: [
15179                 {
15180                     tag: 'input',
15181                     type : 'hidden',
15182                     cls: 'form-hidden-field'
15183                 },
15184                 ibwrap
15185             ]
15186         };
15187         
15188         if(!this.multiple && this.showToggleBtn){
15189             
15190             var caret = {
15191                         tag: 'span',
15192                         cls: 'caret'
15193             };
15194             
15195             if (this.caret != false) {
15196                 caret = {
15197                      tag: 'i',
15198                      cls: 'fa fa-' + this.caret
15199                 };
15200                 
15201             }
15202             
15203             combobox.cn.push({
15204                 tag :'span',
15205                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15206                 cn : [
15207                     caret,
15208                     {
15209                         tag: 'span',
15210                         cls: 'combobox-clear',
15211                         cn  : [
15212                             {
15213                                 tag : 'i',
15214                                 cls: 'icon-remove'
15215                             }
15216                         ]
15217                     }
15218                 ]
15219
15220             })
15221         }
15222         
15223         if(this.multiple){
15224             combobox.cls += ' roo-select2-container-multi';
15225         }
15226         
15227         var align = this.labelAlign || this.parentLabelAlign();
15228         
15229         if (align ==='left' && this.fieldLabel.length) {
15230
15231             cfg.cn = [
15232                 {
15233                    tag : 'i',
15234                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15235                    tooltip : 'This field is required'
15236                 },
15237                 {
15238                     tag: 'label',
15239                     cls : 'control-label col-form-label',
15240                     html : this.fieldLabel
15241
15242                 },
15243                 {
15244                     cls : '', 
15245                     cn: [
15246                         combobox
15247                     ]
15248                 }
15249             ];
15250             
15251             var labelCfg = cfg.cn[1];
15252             var contentCfg = cfg.cn[2];
15253             
15254
15255             if(this.indicatorpos == 'right'){
15256                 cfg.cn = [
15257                     {
15258                         tag: 'label',
15259                         'for' :  id,
15260                         cls : 'control-label col-form-label',
15261                         cn : [
15262                             {
15263                                 tag : 'span',
15264                                 html : this.fieldLabel
15265                             },
15266                             {
15267                                 tag : 'i',
15268                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15269                                 tooltip : 'This field is required'
15270                             }
15271                         ]
15272                     },
15273                     {
15274                         cls : "",
15275                         cn: [
15276                             combobox
15277                         ]
15278                     }
15279
15280                 ];
15281                 
15282                 labelCfg = cfg.cn[0];
15283                 contentCfg = cfg.cn[1];
15284             }
15285             
15286            
15287             
15288             if(this.labelWidth > 12){
15289                 labelCfg.style = "width: " + this.labelWidth + 'px';
15290             }
15291             
15292             if(this.labelWidth < 13 && this.labelmd == 0){
15293                 this.labelmd = this.labelWidth;
15294             }
15295             
15296             if(this.labellg > 0){
15297                 labelCfg.cls += ' col-lg-' + this.labellg;
15298                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15299             }
15300             
15301             if(this.labelmd > 0){
15302                 labelCfg.cls += ' col-md-' + this.labelmd;
15303                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15304             }
15305             
15306             if(this.labelsm > 0){
15307                 labelCfg.cls += ' col-sm-' + this.labelsm;
15308                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15309             }
15310             
15311             if(this.labelxs > 0){
15312                 labelCfg.cls += ' col-xs-' + this.labelxs;
15313                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15314             }
15315                 
15316                 
15317         } else if ( this.fieldLabel.length) {
15318             cfg.cn = [
15319                 {
15320                    tag : 'i',
15321                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15322                    tooltip : 'This field is required'
15323                 },
15324                 {
15325                     tag: 'label',
15326                     cls : 'control-label',
15327                     html : this.fieldLabel
15328
15329                 },
15330                 {
15331                     cls : '', 
15332                     cn: [
15333                         combobox
15334                     ]
15335                 }
15336             ];
15337             
15338             if(this.indicatorpos == 'right'){
15339                 cfg.cn = [
15340                     {
15341                         tag: 'label',
15342                         cls : 'control-label',
15343                         html : this.fieldLabel,
15344                         cn : [
15345                             {
15346                                tag : 'i',
15347                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15348                                tooltip : 'This field is required'
15349                             }
15350                         ]
15351                     },
15352                     {
15353                         cls : '', 
15354                         cn: [
15355                             combobox
15356                         ]
15357                     }
15358                 ];
15359             }
15360         } else {
15361             cfg.cn = combobox;    
15362         }
15363         
15364         
15365         var settings = this;
15366         
15367         ['xs','sm','md','lg'].map(function(size){
15368             if (settings[size]) {
15369                 cfg.cls += ' col-' + size + '-' + settings[size];
15370             }
15371         });
15372         
15373         return cfg;
15374     },
15375     
15376     initTouchView : function()
15377     {
15378         this.renderTouchView();
15379         
15380         this.touchViewEl.on('scroll', function(){
15381             this.el.dom.scrollTop = 0;
15382         }, this);
15383         
15384         this.originalValue = this.getValue();
15385         
15386         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15387         
15388         this.inputEl().on("click", this.showTouchView, this);
15389         if (this.triggerEl) {
15390             this.triggerEl.on("click", this.showTouchView, this);
15391         }
15392         
15393         
15394         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15395         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15396         
15397         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15398         
15399         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15400         this.store.on('load', this.onTouchViewLoad, this);
15401         this.store.on('loadexception', this.onTouchViewLoadException, this);
15402         
15403         if(this.hiddenName){
15404             
15405             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15406             
15407             this.hiddenField.dom.value =
15408                 this.hiddenValue !== undefined ? this.hiddenValue :
15409                 this.value !== undefined ? this.value : '';
15410         
15411             this.el.dom.removeAttribute('name');
15412             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15413         }
15414         
15415         if(this.multiple){
15416             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15417             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15418         }
15419         
15420         if(this.removable && !this.multiple){
15421             var close = this.closeTriggerEl();
15422             if(close){
15423                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15424                 close.on('click', this.removeBtnClick, this, close);
15425             }
15426         }
15427         /*
15428          * fix the bug in Safari iOS8
15429          */
15430         this.inputEl().on("focus", function(e){
15431             document.activeElement.blur();
15432         }, this);
15433         
15434         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15435         
15436         return;
15437         
15438         
15439     },
15440     
15441     renderTouchView : function()
15442     {
15443         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15444         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15445         
15446         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15447         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15448         
15449         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15450         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15451         this.touchViewBodyEl.setStyle('overflow', 'auto');
15452         
15453         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15454         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15455         
15456         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15457         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15458         
15459     },
15460     
15461     showTouchView : function()
15462     {
15463         if(this.disabled){
15464             return;
15465         }
15466         
15467         this.touchViewHeaderEl.hide();
15468
15469         if(this.modalTitle.length){
15470             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15471             this.touchViewHeaderEl.show();
15472         }
15473
15474         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15475         this.touchViewEl.show();
15476
15477         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15478         
15479         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15480         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15481
15482         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15483
15484         if(this.modalTitle.length){
15485             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15486         }
15487         
15488         this.touchViewBodyEl.setHeight(bodyHeight);
15489
15490         if(this.animate){
15491             var _this = this;
15492             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15493         }else{
15494             this.touchViewEl.addClass('in');
15495         }
15496         
15497         if(this._touchViewMask){
15498             Roo.get(document.body).addClass("x-body-masked");
15499             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15500             this._touchViewMask.setStyle('z-index', 10000);
15501             this._touchViewMask.addClass('show');
15502         }
15503         
15504         this.doTouchViewQuery();
15505         
15506     },
15507     
15508     hideTouchView : function()
15509     {
15510         this.touchViewEl.removeClass('in');
15511
15512         if(this.animate){
15513             var _this = this;
15514             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15515         }else{
15516             this.touchViewEl.setStyle('display', 'none');
15517         }
15518         
15519         if(this._touchViewMask){
15520             this._touchViewMask.removeClass('show');
15521             Roo.get(document.body).removeClass("x-body-masked");
15522         }
15523     },
15524     
15525     setTouchViewValue : function()
15526     {
15527         if(this.multiple){
15528             this.clearItem();
15529         
15530             var _this = this;
15531
15532             Roo.each(this.tickItems, function(o){
15533                 this.addItem(o);
15534             }, this);
15535         }
15536         
15537         this.hideTouchView();
15538     },
15539     
15540     doTouchViewQuery : function()
15541     {
15542         var qe = {
15543             query: '',
15544             forceAll: true,
15545             combo: this,
15546             cancel:false
15547         };
15548         
15549         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15550             return false;
15551         }
15552         
15553         if(!this.alwaysQuery || this.mode == 'local'){
15554             this.onTouchViewLoad();
15555             return;
15556         }
15557         
15558         this.store.load();
15559     },
15560     
15561     onTouchViewBeforeLoad : function(combo,opts)
15562     {
15563         return;
15564     },
15565
15566     // private
15567     onTouchViewLoad : function()
15568     {
15569         if(this.store.getCount() < 1){
15570             this.onTouchViewEmptyResults();
15571             return;
15572         }
15573         
15574         this.clearTouchView();
15575         
15576         var rawValue = this.getRawValue();
15577         
15578         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15579         
15580         this.tickItems = [];
15581         
15582         this.store.data.each(function(d, rowIndex){
15583             var row = this.touchViewListGroup.createChild(template);
15584             
15585             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15586                 row.addClass(d.data.cls);
15587             }
15588             
15589             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15590                 var cfg = {
15591                     data : d.data,
15592                     html : d.data[this.displayField]
15593                 };
15594                 
15595                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15596                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15597                 }
15598             }
15599             row.removeClass('selected');
15600             if(!this.multiple && this.valueField &&
15601                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15602             {
15603                 // radio buttons..
15604                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15605                 row.addClass('selected');
15606             }
15607             
15608             if(this.multiple && this.valueField &&
15609                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15610             {
15611                 
15612                 // checkboxes...
15613                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15614                 this.tickItems.push(d.data);
15615             }
15616             
15617             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15618             
15619         }, this);
15620         
15621         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15622         
15623         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15624
15625         if(this.modalTitle.length){
15626             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15627         }
15628
15629         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15630         
15631         if(this.mobile_restrict_height && listHeight < bodyHeight){
15632             this.touchViewBodyEl.setHeight(listHeight);
15633         }
15634         
15635         var _this = this;
15636         
15637         if(firstChecked && listHeight > bodyHeight){
15638             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15639         }
15640         
15641     },
15642     
15643     onTouchViewLoadException : function()
15644     {
15645         this.hideTouchView();
15646     },
15647     
15648     onTouchViewEmptyResults : function()
15649     {
15650         this.clearTouchView();
15651         
15652         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15653         
15654         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15655         
15656     },
15657     
15658     clearTouchView : function()
15659     {
15660         this.touchViewListGroup.dom.innerHTML = '';
15661     },
15662     
15663     onTouchViewClick : function(e, el, o)
15664     {
15665         e.preventDefault();
15666         
15667         var row = o.row;
15668         var rowIndex = o.rowIndex;
15669         
15670         var r = this.store.getAt(rowIndex);
15671         
15672         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15673             
15674             if(!this.multiple){
15675                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15676                     c.dom.removeAttribute('checked');
15677                 }, this);
15678
15679                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15680
15681                 this.setFromData(r.data);
15682
15683                 var close = this.closeTriggerEl();
15684
15685                 if(close){
15686                     close.show();
15687                 }
15688
15689                 this.hideTouchView();
15690
15691                 this.fireEvent('select', this, r, rowIndex);
15692
15693                 return;
15694             }
15695
15696             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15697                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15698                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15699                 return;
15700             }
15701
15702             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15703             this.addItem(r.data);
15704             this.tickItems.push(r.data);
15705         }
15706     },
15707     
15708     getAutoCreateNativeIOS : function()
15709     {
15710         var cfg = {
15711             cls: 'form-group' //input-group,
15712         };
15713         
15714         var combobox =  {
15715             tag: 'select',
15716             cls : 'roo-ios-select'
15717         };
15718         
15719         if (this.name) {
15720             combobox.name = this.name;
15721         }
15722         
15723         if (this.disabled) {
15724             combobox.disabled = true;
15725         }
15726         
15727         var settings = this;
15728         
15729         ['xs','sm','md','lg'].map(function(size){
15730             if (settings[size]) {
15731                 cfg.cls += ' col-' + size + '-' + settings[size];
15732             }
15733         });
15734         
15735         cfg.cn = combobox;
15736         
15737         return cfg;
15738         
15739     },
15740     
15741     initIOSView : function()
15742     {
15743         this.store.on('load', this.onIOSViewLoad, this);
15744         
15745         return;
15746     },
15747     
15748     onIOSViewLoad : function()
15749     {
15750         if(this.store.getCount() < 1){
15751             return;
15752         }
15753         
15754         this.clearIOSView();
15755         
15756         if(this.allowBlank) {
15757             
15758             var default_text = '-- SELECT --';
15759             
15760             if(this.placeholder.length){
15761                 default_text = this.placeholder;
15762             }
15763             
15764             if(this.emptyTitle.length){
15765                 default_text += ' - ' + this.emptyTitle + ' -';
15766             }
15767             
15768             var opt = this.inputEl().createChild({
15769                 tag: 'option',
15770                 value : 0,
15771                 html : default_text
15772             });
15773             
15774             var o = {};
15775             o[this.valueField] = 0;
15776             o[this.displayField] = default_text;
15777             
15778             this.ios_options.push({
15779                 data : o,
15780                 el : opt
15781             });
15782             
15783         }
15784         
15785         this.store.data.each(function(d, rowIndex){
15786             
15787             var html = '';
15788             
15789             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15790                 html = d.data[this.displayField];
15791             }
15792             
15793             var value = '';
15794             
15795             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15796                 value = d.data[this.valueField];
15797             }
15798             
15799             var option = {
15800                 tag: 'option',
15801                 value : value,
15802                 html : html
15803             };
15804             
15805             if(this.value == d.data[this.valueField]){
15806                 option['selected'] = true;
15807             }
15808             
15809             var opt = this.inputEl().createChild(option);
15810             
15811             this.ios_options.push({
15812                 data : d.data,
15813                 el : opt
15814             });
15815             
15816         }, this);
15817         
15818         this.inputEl().on('change', function(){
15819            this.fireEvent('select', this);
15820         }, this);
15821         
15822     },
15823     
15824     clearIOSView: function()
15825     {
15826         this.inputEl().dom.innerHTML = '';
15827         
15828         this.ios_options = [];
15829     },
15830     
15831     setIOSValue: function(v)
15832     {
15833         this.value = v;
15834         
15835         if(!this.ios_options){
15836             return;
15837         }
15838         
15839         Roo.each(this.ios_options, function(opts){
15840            
15841            opts.el.dom.removeAttribute('selected');
15842            
15843            if(opts.data[this.valueField] != v){
15844                return;
15845            }
15846            
15847            opts.el.dom.setAttribute('selected', true);
15848            
15849         }, this);
15850     }
15851
15852     /** 
15853     * @cfg {Boolean} grow 
15854     * @hide 
15855     */
15856     /** 
15857     * @cfg {Number} growMin 
15858     * @hide 
15859     */
15860     /** 
15861     * @cfg {Number} growMax 
15862     * @hide 
15863     */
15864     /**
15865      * @hide
15866      * @method autoSize
15867      */
15868 });
15869
15870 Roo.apply(Roo.bootstrap.ComboBox,  {
15871     
15872     header : {
15873         tag: 'div',
15874         cls: 'modal-header',
15875         cn: [
15876             {
15877                 tag: 'h4',
15878                 cls: 'modal-title'
15879             }
15880         ]
15881     },
15882     
15883     body : {
15884         tag: 'div',
15885         cls: 'modal-body',
15886         cn: [
15887             {
15888                 tag: 'ul',
15889                 cls: 'list-group'
15890             }
15891         ]
15892     },
15893     
15894     listItemRadio : {
15895         tag: 'li',
15896         cls: 'list-group-item',
15897         cn: [
15898             {
15899                 tag: 'span',
15900                 cls: 'roo-combobox-list-group-item-value'
15901             },
15902             {
15903                 tag: 'div',
15904                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15905                 cn: [
15906                     {
15907                         tag: 'input',
15908                         type: 'radio'
15909                     },
15910                     {
15911                         tag: 'label'
15912                     }
15913                 ]
15914             }
15915         ]
15916     },
15917     
15918     listItemCheckbox : {
15919         tag: 'li',
15920         cls: 'list-group-item',
15921         cn: [
15922             {
15923                 tag: 'span',
15924                 cls: 'roo-combobox-list-group-item-value'
15925             },
15926             {
15927                 tag: 'div',
15928                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15929                 cn: [
15930                     {
15931                         tag: 'input',
15932                         type: 'checkbox'
15933                     },
15934                     {
15935                         tag: 'label'
15936                     }
15937                 ]
15938             }
15939         ]
15940     },
15941     
15942     emptyResult : {
15943         tag: 'div',
15944         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15945     },
15946     
15947     footer : {
15948         tag: 'div',
15949         cls: 'modal-footer',
15950         cn: [
15951             {
15952                 tag: 'div',
15953                 cls: 'row',
15954                 cn: [
15955                     {
15956                         tag: 'div',
15957                         cls: 'col-xs-6 text-left',
15958                         cn: {
15959                             tag: 'button',
15960                             cls: 'btn btn-danger roo-touch-view-cancel',
15961                             html: 'Cancel'
15962                         }
15963                     },
15964                     {
15965                         tag: 'div',
15966                         cls: 'col-xs-6 text-right',
15967                         cn: {
15968                             tag: 'button',
15969                             cls: 'btn btn-success roo-touch-view-ok',
15970                             html: 'OK'
15971                         }
15972                     }
15973                 ]
15974             }
15975         ]
15976         
15977     }
15978 });
15979
15980 Roo.apply(Roo.bootstrap.ComboBox,  {
15981     
15982     touchViewTemplate : {
15983         tag: 'div',
15984         cls: 'modal fade roo-combobox-touch-view',
15985         cn: [
15986             {
15987                 tag: 'div',
15988                 cls: 'modal-dialog',
15989                 style : 'position:fixed', // we have to fix position....
15990                 cn: [
15991                     {
15992                         tag: 'div',
15993                         cls: 'modal-content',
15994                         cn: [
15995                             Roo.bootstrap.ComboBox.header,
15996                             Roo.bootstrap.ComboBox.body,
15997                             Roo.bootstrap.ComboBox.footer
15998                         ]
15999                     }
16000                 ]
16001             }
16002         ]
16003     }
16004 });/*
16005  * Based on:
16006  * Ext JS Library 1.1.1
16007  * Copyright(c) 2006-2007, Ext JS, LLC.
16008  *
16009  * Originally Released Under LGPL - original licence link has changed is not relivant.
16010  *
16011  * Fork - LGPL
16012  * <script type="text/javascript">
16013  */
16014
16015 /**
16016  * @class Roo.View
16017  * @extends Roo.util.Observable
16018  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16019  * This class also supports single and multi selection modes. <br>
16020  * Create a data model bound view:
16021  <pre><code>
16022  var store = new Roo.data.Store(...);
16023
16024  var view = new Roo.View({
16025     el : "my-element",
16026     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16027  
16028     singleSelect: true,
16029     selectedClass: "ydataview-selected",
16030     store: store
16031  });
16032
16033  // listen for node click?
16034  view.on("click", function(vw, index, node, e){
16035  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16036  });
16037
16038  // load XML data
16039  dataModel.load("foobar.xml");
16040  </code></pre>
16041  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16042  * <br><br>
16043  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16044  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16045  * 
16046  * Note: old style constructor is still suported (container, template, config)
16047  * 
16048  * @constructor
16049  * Create a new View
16050  * @param {Object} config The config object
16051  * 
16052  */
16053 Roo.View = function(config, depreciated_tpl, depreciated_config){
16054     
16055     this.parent = false;
16056     
16057     if (typeof(depreciated_tpl) == 'undefined') {
16058         // new way.. - universal constructor.
16059         Roo.apply(this, config);
16060         this.el  = Roo.get(this.el);
16061     } else {
16062         // old format..
16063         this.el  = Roo.get(config);
16064         this.tpl = depreciated_tpl;
16065         Roo.apply(this, depreciated_config);
16066     }
16067     this.wrapEl  = this.el.wrap().wrap();
16068     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16069     
16070     
16071     if(typeof(this.tpl) == "string"){
16072         this.tpl = new Roo.Template(this.tpl);
16073     } else {
16074         // support xtype ctors..
16075         this.tpl = new Roo.factory(this.tpl, Roo);
16076     }
16077     
16078     
16079     this.tpl.compile();
16080     
16081     /** @private */
16082     this.addEvents({
16083         /**
16084          * @event beforeclick
16085          * Fires before a click is processed. Returns false to cancel the default action.
16086          * @param {Roo.View} this
16087          * @param {Number} index The index of the target node
16088          * @param {HTMLElement} node The target node
16089          * @param {Roo.EventObject} e The raw event object
16090          */
16091             "beforeclick" : true,
16092         /**
16093          * @event click
16094          * Fires when a template node is clicked.
16095          * @param {Roo.View} this
16096          * @param {Number} index The index of the target node
16097          * @param {HTMLElement} node The target node
16098          * @param {Roo.EventObject} e The raw event object
16099          */
16100             "click" : true,
16101         /**
16102          * @event dblclick
16103          * Fires when a template node is double clicked.
16104          * @param {Roo.View} this
16105          * @param {Number} index The index of the target node
16106          * @param {HTMLElement} node The target node
16107          * @param {Roo.EventObject} e The raw event object
16108          */
16109             "dblclick" : true,
16110         /**
16111          * @event contextmenu
16112          * Fires when a template node is right clicked.
16113          * @param {Roo.View} this
16114          * @param {Number} index The index of the target node
16115          * @param {HTMLElement} node The target node
16116          * @param {Roo.EventObject} e The raw event object
16117          */
16118             "contextmenu" : true,
16119         /**
16120          * @event selectionchange
16121          * Fires when the selected nodes change.
16122          * @param {Roo.View} this
16123          * @param {Array} selections Array of the selected nodes
16124          */
16125             "selectionchange" : true,
16126     
16127         /**
16128          * @event beforeselect
16129          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16130          * @param {Roo.View} this
16131          * @param {HTMLElement} node The node to be selected
16132          * @param {Array} selections Array of currently selected nodes
16133          */
16134             "beforeselect" : true,
16135         /**
16136          * @event preparedata
16137          * Fires on every row to render, to allow you to change the data.
16138          * @param {Roo.View} this
16139          * @param {Object} data to be rendered (change this)
16140          */
16141           "preparedata" : true
16142           
16143           
16144         });
16145
16146
16147
16148     this.el.on({
16149         "click": this.onClick,
16150         "dblclick": this.onDblClick,
16151         "contextmenu": this.onContextMenu,
16152         scope:this
16153     });
16154
16155     this.selections = [];
16156     this.nodes = [];
16157     this.cmp = new Roo.CompositeElementLite([]);
16158     if(this.store){
16159         this.store = Roo.factory(this.store, Roo.data);
16160         this.setStore(this.store, true);
16161     }
16162     
16163     if ( this.footer && this.footer.xtype) {
16164            
16165          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16166         
16167         this.footer.dataSource = this.store;
16168         this.footer.container = fctr;
16169         this.footer = Roo.factory(this.footer, Roo);
16170         fctr.insertFirst(this.el);
16171         
16172         // this is a bit insane - as the paging toolbar seems to detach the el..
16173 //        dom.parentNode.parentNode.parentNode
16174          // they get detached?
16175     }
16176     
16177     
16178     Roo.View.superclass.constructor.call(this);
16179     
16180     
16181 };
16182
16183 Roo.extend(Roo.View, Roo.util.Observable, {
16184     
16185      /**
16186      * @cfg {Roo.data.Store} store Data store to load data from.
16187      */
16188     store : false,
16189     
16190     /**
16191      * @cfg {String|Roo.Element} el The container element.
16192      */
16193     el : '',
16194     
16195     /**
16196      * @cfg {String|Roo.Template} tpl The template used by this View 
16197      */
16198     tpl : false,
16199     /**
16200      * @cfg {String} dataName the named area of the template to use as the data area
16201      *                          Works with domtemplates roo-name="name"
16202      */
16203     dataName: false,
16204     /**
16205      * @cfg {String} selectedClass The css class to add to selected nodes
16206      */
16207     selectedClass : "x-view-selected",
16208      /**
16209      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16210      */
16211     emptyText : "",
16212     
16213     /**
16214      * @cfg {String} text to display on mask (default Loading)
16215      */
16216     mask : false,
16217     /**
16218      * @cfg {Boolean} multiSelect Allow multiple selection
16219      */
16220     multiSelect : false,
16221     /**
16222      * @cfg {Boolean} singleSelect Allow single selection
16223      */
16224     singleSelect:  false,
16225     
16226     /**
16227      * @cfg {Boolean} toggleSelect - selecting 
16228      */
16229     toggleSelect : false,
16230     
16231     /**
16232      * @cfg {Boolean} tickable - selecting 
16233      */
16234     tickable : false,
16235     
16236     /**
16237      * Returns the element this view is bound to.
16238      * @return {Roo.Element}
16239      */
16240     getEl : function(){
16241         return this.wrapEl;
16242     },
16243     
16244     
16245
16246     /**
16247      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16248      */
16249     refresh : function(){
16250         //Roo.log('refresh');
16251         var t = this.tpl;
16252         
16253         // if we are using something like 'domtemplate', then
16254         // the what gets used is:
16255         // t.applySubtemplate(NAME, data, wrapping data..)
16256         // the outer template then get' applied with
16257         //     the store 'extra data'
16258         // and the body get's added to the
16259         //      roo-name="data" node?
16260         //      <span class='roo-tpl-{name}'></span> ?????
16261         
16262         
16263         
16264         this.clearSelections();
16265         this.el.update("");
16266         var html = [];
16267         var records = this.store.getRange();
16268         if(records.length < 1) {
16269             
16270             // is this valid??  = should it render a template??
16271             
16272             this.el.update(this.emptyText);
16273             return;
16274         }
16275         var el = this.el;
16276         if (this.dataName) {
16277             this.el.update(t.apply(this.store.meta)); //????
16278             el = this.el.child('.roo-tpl-' + this.dataName);
16279         }
16280         
16281         for(var i = 0, len = records.length; i < len; i++){
16282             var data = this.prepareData(records[i].data, i, records[i]);
16283             this.fireEvent("preparedata", this, data, i, records[i]);
16284             
16285             var d = Roo.apply({}, data);
16286             
16287             if(this.tickable){
16288                 Roo.apply(d, {'roo-id' : Roo.id()});
16289                 
16290                 var _this = this;
16291             
16292                 Roo.each(this.parent.item, function(item){
16293                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16294                         return;
16295                     }
16296                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16297                 });
16298             }
16299             
16300             html[html.length] = Roo.util.Format.trim(
16301                 this.dataName ?
16302                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16303                     t.apply(d)
16304             );
16305         }
16306         
16307         
16308         
16309         el.update(html.join(""));
16310         this.nodes = el.dom.childNodes;
16311         this.updateIndexes(0);
16312     },
16313     
16314
16315     /**
16316      * Function to override to reformat the data that is sent to
16317      * the template for each node.
16318      * DEPRICATED - use the preparedata event handler.
16319      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16320      * a JSON object for an UpdateManager bound view).
16321      */
16322     prepareData : function(data, index, record)
16323     {
16324         this.fireEvent("preparedata", this, data, index, record);
16325         return data;
16326     },
16327
16328     onUpdate : function(ds, record){
16329         // Roo.log('on update');   
16330         this.clearSelections();
16331         var index = this.store.indexOf(record);
16332         var n = this.nodes[index];
16333         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16334         n.parentNode.removeChild(n);
16335         this.updateIndexes(index, index);
16336     },
16337
16338     
16339     
16340 // --------- FIXME     
16341     onAdd : function(ds, records, index)
16342     {
16343         //Roo.log(['on Add', ds, records, index] );        
16344         this.clearSelections();
16345         if(this.nodes.length == 0){
16346             this.refresh();
16347             return;
16348         }
16349         var n = this.nodes[index];
16350         for(var i = 0, len = records.length; i < len; i++){
16351             var d = this.prepareData(records[i].data, i, records[i]);
16352             if(n){
16353                 this.tpl.insertBefore(n, d);
16354             }else{
16355                 
16356                 this.tpl.append(this.el, d);
16357             }
16358         }
16359         this.updateIndexes(index);
16360     },
16361
16362     onRemove : function(ds, record, index){
16363        // Roo.log('onRemove');
16364         this.clearSelections();
16365         var el = this.dataName  ?
16366             this.el.child('.roo-tpl-' + this.dataName) :
16367             this.el; 
16368         
16369         el.dom.removeChild(this.nodes[index]);
16370         this.updateIndexes(index);
16371     },
16372
16373     /**
16374      * Refresh an individual node.
16375      * @param {Number} index
16376      */
16377     refreshNode : function(index){
16378         this.onUpdate(this.store, this.store.getAt(index));
16379     },
16380
16381     updateIndexes : function(startIndex, endIndex){
16382         var ns = this.nodes;
16383         startIndex = startIndex || 0;
16384         endIndex = endIndex || ns.length - 1;
16385         for(var i = startIndex; i <= endIndex; i++){
16386             ns[i].nodeIndex = i;
16387         }
16388     },
16389
16390     /**
16391      * Changes the data store this view uses and refresh the view.
16392      * @param {Store} store
16393      */
16394     setStore : function(store, initial){
16395         if(!initial && this.store){
16396             this.store.un("datachanged", this.refresh);
16397             this.store.un("add", this.onAdd);
16398             this.store.un("remove", this.onRemove);
16399             this.store.un("update", this.onUpdate);
16400             this.store.un("clear", this.refresh);
16401             this.store.un("beforeload", this.onBeforeLoad);
16402             this.store.un("load", this.onLoad);
16403             this.store.un("loadexception", this.onLoad);
16404         }
16405         if(store){
16406           
16407             store.on("datachanged", this.refresh, this);
16408             store.on("add", this.onAdd, this);
16409             store.on("remove", this.onRemove, this);
16410             store.on("update", this.onUpdate, this);
16411             store.on("clear", this.refresh, this);
16412             store.on("beforeload", this.onBeforeLoad, this);
16413             store.on("load", this.onLoad, this);
16414             store.on("loadexception", this.onLoad, this);
16415         }
16416         
16417         if(store){
16418             this.refresh();
16419         }
16420     },
16421     /**
16422      * onbeforeLoad - masks the loading area.
16423      *
16424      */
16425     onBeforeLoad : function(store,opts)
16426     {
16427          //Roo.log('onBeforeLoad');   
16428         if (!opts.add) {
16429             this.el.update("");
16430         }
16431         this.el.mask(this.mask ? this.mask : "Loading" ); 
16432     },
16433     onLoad : function ()
16434     {
16435         this.el.unmask();
16436     },
16437     
16438
16439     /**
16440      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16441      * @param {HTMLElement} node
16442      * @return {HTMLElement} The template node
16443      */
16444     findItemFromChild : function(node){
16445         var el = this.dataName  ?
16446             this.el.child('.roo-tpl-' + this.dataName,true) :
16447             this.el.dom; 
16448         
16449         if(!node || node.parentNode == el){
16450                     return node;
16451             }
16452             var p = node.parentNode;
16453             while(p && p != el){
16454             if(p.parentNode == el){
16455                 return p;
16456             }
16457             p = p.parentNode;
16458         }
16459             return null;
16460     },
16461
16462     /** @ignore */
16463     onClick : function(e){
16464         var item = this.findItemFromChild(e.getTarget());
16465         if(item){
16466             var index = this.indexOf(item);
16467             if(this.onItemClick(item, index, e) !== false){
16468                 this.fireEvent("click", this, index, item, e);
16469             }
16470         }else{
16471             this.clearSelections();
16472         }
16473     },
16474
16475     /** @ignore */
16476     onContextMenu : function(e){
16477         var item = this.findItemFromChild(e.getTarget());
16478         if(item){
16479             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16480         }
16481     },
16482
16483     /** @ignore */
16484     onDblClick : function(e){
16485         var item = this.findItemFromChild(e.getTarget());
16486         if(item){
16487             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16488         }
16489     },
16490
16491     onItemClick : function(item, index, e)
16492     {
16493         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16494             return false;
16495         }
16496         if (this.toggleSelect) {
16497             var m = this.isSelected(item) ? 'unselect' : 'select';
16498             //Roo.log(m);
16499             var _t = this;
16500             _t[m](item, true, false);
16501             return true;
16502         }
16503         if(this.multiSelect || this.singleSelect){
16504             if(this.multiSelect && e.shiftKey && this.lastSelection){
16505                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16506             }else{
16507                 this.select(item, this.multiSelect && e.ctrlKey);
16508                 this.lastSelection = item;
16509             }
16510             
16511             if(!this.tickable){
16512                 e.preventDefault();
16513             }
16514             
16515         }
16516         return true;
16517     },
16518
16519     /**
16520      * Get the number of selected nodes.
16521      * @return {Number}
16522      */
16523     getSelectionCount : function(){
16524         return this.selections.length;
16525     },
16526
16527     /**
16528      * Get the currently selected nodes.
16529      * @return {Array} An array of HTMLElements
16530      */
16531     getSelectedNodes : function(){
16532         return this.selections;
16533     },
16534
16535     /**
16536      * Get the indexes of the selected nodes.
16537      * @return {Array}
16538      */
16539     getSelectedIndexes : function(){
16540         var indexes = [], s = this.selections;
16541         for(var i = 0, len = s.length; i < len; i++){
16542             indexes.push(s[i].nodeIndex);
16543         }
16544         return indexes;
16545     },
16546
16547     /**
16548      * Clear all selections
16549      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16550      */
16551     clearSelections : function(suppressEvent){
16552         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16553             this.cmp.elements = this.selections;
16554             this.cmp.removeClass(this.selectedClass);
16555             this.selections = [];
16556             if(!suppressEvent){
16557                 this.fireEvent("selectionchange", this, this.selections);
16558             }
16559         }
16560     },
16561
16562     /**
16563      * Returns true if the passed node is selected
16564      * @param {HTMLElement/Number} node The node or node index
16565      * @return {Boolean}
16566      */
16567     isSelected : function(node){
16568         var s = this.selections;
16569         if(s.length < 1){
16570             return false;
16571         }
16572         node = this.getNode(node);
16573         return s.indexOf(node) !== -1;
16574     },
16575
16576     /**
16577      * Selects nodes.
16578      * @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
16579      * @param {Boolean} keepExisting (optional) true to keep existing selections
16580      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16581      */
16582     select : function(nodeInfo, keepExisting, suppressEvent){
16583         if(nodeInfo instanceof Array){
16584             if(!keepExisting){
16585                 this.clearSelections(true);
16586             }
16587             for(var i = 0, len = nodeInfo.length; i < len; i++){
16588                 this.select(nodeInfo[i], true, true);
16589             }
16590             return;
16591         } 
16592         var node = this.getNode(nodeInfo);
16593         if(!node || this.isSelected(node)){
16594             return; // already selected.
16595         }
16596         if(!keepExisting){
16597             this.clearSelections(true);
16598         }
16599         
16600         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16601             Roo.fly(node).addClass(this.selectedClass);
16602             this.selections.push(node);
16603             if(!suppressEvent){
16604                 this.fireEvent("selectionchange", this, this.selections);
16605             }
16606         }
16607         
16608         
16609     },
16610       /**
16611      * Unselects nodes.
16612      * @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
16613      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16614      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16615      */
16616     unselect : function(nodeInfo, keepExisting, suppressEvent)
16617     {
16618         if(nodeInfo instanceof Array){
16619             Roo.each(this.selections, function(s) {
16620                 this.unselect(s, nodeInfo);
16621             }, this);
16622             return;
16623         }
16624         var node = this.getNode(nodeInfo);
16625         if(!node || !this.isSelected(node)){
16626             //Roo.log("not selected");
16627             return; // not selected.
16628         }
16629         // fireevent???
16630         var ns = [];
16631         Roo.each(this.selections, function(s) {
16632             if (s == node ) {
16633                 Roo.fly(node).removeClass(this.selectedClass);
16634
16635                 return;
16636             }
16637             ns.push(s);
16638         },this);
16639         
16640         this.selections= ns;
16641         this.fireEvent("selectionchange", this, this.selections);
16642     },
16643
16644     /**
16645      * Gets a template node.
16646      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16647      * @return {HTMLElement} The node or null if it wasn't found
16648      */
16649     getNode : function(nodeInfo){
16650         if(typeof nodeInfo == "string"){
16651             return document.getElementById(nodeInfo);
16652         }else if(typeof nodeInfo == "number"){
16653             return this.nodes[nodeInfo];
16654         }
16655         return nodeInfo;
16656     },
16657
16658     /**
16659      * Gets a range template nodes.
16660      * @param {Number} startIndex
16661      * @param {Number} endIndex
16662      * @return {Array} An array of nodes
16663      */
16664     getNodes : function(start, end){
16665         var ns = this.nodes;
16666         start = start || 0;
16667         end = typeof end == "undefined" ? ns.length - 1 : end;
16668         var nodes = [];
16669         if(start <= end){
16670             for(var i = start; i <= end; i++){
16671                 nodes.push(ns[i]);
16672             }
16673         } else{
16674             for(var i = start; i >= end; i--){
16675                 nodes.push(ns[i]);
16676             }
16677         }
16678         return nodes;
16679     },
16680
16681     /**
16682      * Finds the index of the passed node
16683      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16684      * @return {Number} The index of the node or -1
16685      */
16686     indexOf : function(node){
16687         node = this.getNode(node);
16688         if(typeof node.nodeIndex == "number"){
16689             return node.nodeIndex;
16690         }
16691         var ns = this.nodes;
16692         for(var i = 0, len = ns.length; i < len; i++){
16693             if(ns[i] == node){
16694                 return i;
16695             }
16696         }
16697         return -1;
16698     }
16699 });
16700 /*
16701  * - LGPL
16702  *
16703  * based on jquery fullcalendar
16704  * 
16705  */
16706
16707 Roo.bootstrap = Roo.bootstrap || {};
16708 /**
16709  * @class Roo.bootstrap.Calendar
16710  * @extends Roo.bootstrap.Component
16711  * Bootstrap Calendar class
16712  * @cfg {Boolean} loadMask (true|false) default false
16713  * @cfg {Object} header generate the user specific header of the calendar, default false
16714
16715  * @constructor
16716  * Create a new Container
16717  * @param {Object} config The config object
16718  */
16719
16720
16721
16722 Roo.bootstrap.Calendar = function(config){
16723     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16724      this.addEvents({
16725         /**
16726              * @event select
16727              * Fires when a date is selected
16728              * @param {DatePicker} this
16729              * @param {Date} date The selected date
16730              */
16731         'select': true,
16732         /**
16733              * @event monthchange
16734              * Fires when the displayed month changes 
16735              * @param {DatePicker} this
16736              * @param {Date} date The selected month
16737              */
16738         'monthchange': true,
16739         /**
16740              * @event evententer
16741              * Fires when mouse over an event
16742              * @param {Calendar} this
16743              * @param {event} Event
16744              */
16745         'evententer': true,
16746         /**
16747              * @event eventleave
16748              * Fires when the mouse leaves an
16749              * @param {Calendar} this
16750              * @param {event}
16751              */
16752         'eventleave': true,
16753         /**
16754              * @event eventclick
16755              * Fires when the mouse click an
16756              * @param {Calendar} this
16757              * @param {event}
16758              */
16759         'eventclick': true
16760         
16761     });
16762
16763 };
16764
16765 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16766     
16767      /**
16768      * @cfg {Number} startDay
16769      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16770      */
16771     startDay : 0,
16772     
16773     loadMask : false,
16774     
16775     header : false,
16776       
16777     getAutoCreate : function(){
16778         
16779         
16780         var fc_button = function(name, corner, style, content ) {
16781             return Roo.apply({},{
16782                 tag : 'span',
16783                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16784                          (corner.length ?
16785                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16786                             ''
16787                         ),
16788                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16789                 unselectable: 'on'
16790             });
16791         };
16792         
16793         var header = {};
16794         
16795         if(!this.header){
16796             header = {
16797                 tag : 'table',
16798                 cls : 'fc-header',
16799                 style : 'width:100%',
16800                 cn : [
16801                     {
16802                         tag: 'tr',
16803                         cn : [
16804                             {
16805                                 tag : 'td',
16806                                 cls : 'fc-header-left',
16807                                 cn : [
16808                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16809                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16810                                     { tag: 'span', cls: 'fc-header-space' },
16811                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16812
16813
16814                                 ]
16815                             },
16816
16817                             {
16818                                 tag : 'td',
16819                                 cls : 'fc-header-center',
16820                                 cn : [
16821                                     {
16822                                         tag: 'span',
16823                                         cls: 'fc-header-title',
16824                                         cn : {
16825                                             tag: 'H2',
16826                                             html : 'month / year'
16827                                         }
16828                                     }
16829
16830                                 ]
16831                             },
16832                             {
16833                                 tag : 'td',
16834                                 cls : 'fc-header-right',
16835                                 cn : [
16836                               /*      fc_button('month', 'left', '', 'month' ),
16837                                     fc_button('week', '', '', 'week' ),
16838                                     fc_button('day', 'right', '', 'day' )
16839                                 */    
16840
16841                                 ]
16842                             }
16843
16844                         ]
16845                     }
16846                 ]
16847             };
16848         }
16849         
16850         header = this.header;
16851         
16852        
16853         var cal_heads = function() {
16854             var ret = [];
16855             // fixme - handle this.
16856             
16857             for (var i =0; i < Date.dayNames.length; i++) {
16858                 var d = Date.dayNames[i];
16859                 ret.push({
16860                     tag: 'th',
16861                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16862                     html : d.substring(0,3)
16863                 });
16864                 
16865             }
16866             ret[0].cls += ' fc-first';
16867             ret[6].cls += ' fc-last';
16868             return ret;
16869         };
16870         var cal_cell = function(n) {
16871             return  {
16872                 tag: 'td',
16873                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16874                 cn : [
16875                     {
16876                         cn : [
16877                             {
16878                                 cls: 'fc-day-number',
16879                                 html: 'D'
16880                             },
16881                             {
16882                                 cls: 'fc-day-content',
16883                              
16884                                 cn : [
16885                                      {
16886                                         style: 'position: relative;' // height: 17px;
16887                                     }
16888                                 ]
16889                             }
16890                             
16891                             
16892                         ]
16893                     }
16894                 ]
16895                 
16896             }
16897         };
16898         var cal_rows = function() {
16899             
16900             var ret = [];
16901             for (var r = 0; r < 6; r++) {
16902                 var row= {
16903                     tag : 'tr',
16904                     cls : 'fc-week',
16905                     cn : []
16906                 };
16907                 
16908                 for (var i =0; i < Date.dayNames.length; i++) {
16909                     var d = Date.dayNames[i];
16910                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16911
16912                 }
16913                 row.cn[0].cls+=' fc-first';
16914                 row.cn[0].cn[0].style = 'min-height:90px';
16915                 row.cn[6].cls+=' fc-last';
16916                 ret.push(row);
16917                 
16918             }
16919             ret[0].cls += ' fc-first';
16920             ret[4].cls += ' fc-prev-last';
16921             ret[5].cls += ' fc-last';
16922             return ret;
16923             
16924         };
16925         
16926         var cal_table = {
16927             tag: 'table',
16928             cls: 'fc-border-separate',
16929             style : 'width:100%',
16930             cellspacing  : 0,
16931             cn : [
16932                 { 
16933                     tag: 'thead',
16934                     cn : [
16935                         { 
16936                             tag: 'tr',
16937                             cls : 'fc-first fc-last',
16938                             cn : cal_heads()
16939                         }
16940                     ]
16941                 },
16942                 { 
16943                     tag: 'tbody',
16944                     cn : cal_rows()
16945                 }
16946                   
16947             ]
16948         };
16949          
16950          var cfg = {
16951             cls : 'fc fc-ltr',
16952             cn : [
16953                 header,
16954                 {
16955                     cls : 'fc-content',
16956                     style : "position: relative;",
16957                     cn : [
16958                         {
16959                             cls : 'fc-view fc-view-month fc-grid',
16960                             style : 'position: relative',
16961                             unselectable : 'on',
16962                             cn : [
16963                                 {
16964                                     cls : 'fc-event-container',
16965                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16966                                 },
16967                                 cal_table
16968                             ]
16969                         }
16970                     ]
16971     
16972                 }
16973            ] 
16974             
16975         };
16976         
16977          
16978         
16979         return cfg;
16980     },
16981     
16982     
16983     initEvents : function()
16984     {
16985         if(!this.store){
16986             throw "can not find store for calendar";
16987         }
16988         
16989         var mark = {
16990             tag: "div",
16991             cls:"x-dlg-mask",
16992             style: "text-align:center",
16993             cn: [
16994                 {
16995                     tag: "div",
16996                     style: "background-color:white;width:50%;margin:250 auto",
16997                     cn: [
16998                         {
16999                             tag: "img",
17000                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17001                         },
17002                         {
17003                             tag: "span",
17004                             html: "Loading"
17005                         }
17006                         
17007                     ]
17008                 }
17009             ]
17010         };
17011         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17012         
17013         var size = this.el.select('.fc-content', true).first().getSize();
17014         this.maskEl.setSize(size.width, size.height);
17015         this.maskEl.enableDisplayMode("block");
17016         if(!this.loadMask){
17017             this.maskEl.hide();
17018         }
17019         
17020         this.store = Roo.factory(this.store, Roo.data);
17021         this.store.on('load', this.onLoad, this);
17022         this.store.on('beforeload', this.onBeforeLoad, this);
17023         
17024         this.resize();
17025         
17026         this.cells = this.el.select('.fc-day',true);
17027         //Roo.log(this.cells);
17028         this.textNodes = this.el.query('.fc-day-number');
17029         this.cells.addClassOnOver('fc-state-hover');
17030         
17031         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17032         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17033         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17034         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17035         
17036         this.on('monthchange', this.onMonthChange, this);
17037         
17038         this.update(new Date().clearTime());
17039     },
17040     
17041     resize : function() {
17042         var sz  = this.el.getSize();
17043         
17044         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17045         this.el.select('.fc-day-content div',true).setHeight(34);
17046     },
17047     
17048     
17049     // private
17050     showPrevMonth : function(e){
17051         this.update(this.activeDate.add("mo", -1));
17052     },
17053     showToday : function(e){
17054         this.update(new Date().clearTime());
17055     },
17056     // private
17057     showNextMonth : function(e){
17058         this.update(this.activeDate.add("mo", 1));
17059     },
17060
17061     // private
17062     showPrevYear : function(){
17063         this.update(this.activeDate.add("y", -1));
17064     },
17065
17066     // private
17067     showNextYear : function(){
17068         this.update(this.activeDate.add("y", 1));
17069     },
17070
17071     
17072    // private
17073     update : function(date)
17074     {
17075         var vd = this.activeDate;
17076         this.activeDate = date;
17077 //        if(vd && this.el){
17078 //            var t = date.getTime();
17079 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17080 //                Roo.log('using add remove');
17081 //                
17082 //                this.fireEvent('monthchange', this, date);
17083 //                
17084 //                this.cells.removeClass("fc-state-highlight");
17085 //                this.cells.each(function(c){
17086 //                   if(c.dateValue == t){
17087 //                       c.addClass("fc-state-highlight");
17088 //                       setTimeout(function(){
17089 //                            try{c.dom.firstChild.focus();}catch(e){}
17090 //                       }, 50);
17091 //                       return false;
17092 //                   }
17093 //                   return true;
17094 //                });
17095 //                return;
17096 //            }
17097 //        }
17098         
17099         var days = date.getDaysInMonth();
17100         
17101         var firstOfMonth = date.getFirstDateOfMonth();
17102         var startingPos = firstOfMonth.getDay()-this.startDay;
17103         
17104         if(startingPos < this.startDay){
17105             startingPos += 7;
17106         }
17107         
17108         var pm = date.add(Date.MONTH, -1);
17109         var prevStart = pm.getDaysInMonth()-startingPos;
17110 //        
17111         this.cells = this.el.select('.fc-day',true);
17112         this.textNodes = this.el.query('.fc-day-number');
17113         this.cells.addClassOnOver('fc-state-hover');
17114         
17115         var cells = this.cells.elements;
17116         var textEls = this.textNodes;
17117         
17118         Roo.each(cells, function(cell){
17119             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17120         });
17121         
17122         days += startingPos;
17123
17124         // convert everything to numbers so it's fast
17125         var day = 86400000;
17126         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17127         //Roo.log(d);
17128         //Roo.log(pm);
17129         //Roo.log(prevStart);
17130         
17131         var today = new Date().clearTime().getTime();
17132         var sel = date.clearTime().getTime();
17133         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17134         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17135         var ddMatch = this.disabledDatesRE;
17136         var ddText = this.disabledDatesText;
17137         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17138         var ddaysText = this.disabledDaysText;
17139         var format = this.format;
17140         
17141         var setCellClass = function(cal, cell){
17142             cell.row = 0;
17143             cell.events = [];
17144             cell.more = [];
17145             //Roo.log('set Cell Class');
17146             cell.title = "";
17147             var t = d.getTime();
17148             
17149             //Roo.log(d);
17150             
17151             cell.dateValue = t;
17152             if(t == today){
17153                 cell.className += " fc-today";
17154                 cell.className += " fc-state-highlight";
17155                 cell.title = cal.todayText;
17156             }
17157             if(t == sel){
17158                 // disable highlight in other month..
17159                 //cell.className += " fc-state-highlight";
17160                 
17161             }
17162             // disabling
17163             if(t < min) {
17164                 cell.className = " fc-state-disabled";
17165                 cell.title = cal.minText;
17166                 return;
17167             }
17168             if(t > max) {
17169                 cell.className = " fc-state-disabled";
17170                 cell.title = cal.maxText;
17171                 return;
17172             }
17173             if(ddays){
17174                 if(ddays.indexOf(d.getDay()) != -1){
17175                     cell.title = ddaysText;
17176                     cell.className = " fc-state-disabled";
17177                 }
17178             }
17179             if(ddMatch && format){
17180                 var fvalue = d.dateFormat(format);
17181                 if(ddMatch.test(fvalue)){
17182                     cell.title = ddText.replace("%0", fvalue);
17183                     cell.className = " fc-state-disabled";
17184                 }
17185             }
17186             
17187             if (!cell.initialClassName) {
17188                 cell.initialClassName = cell.dom.className;
17189             }
17190             
17191             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17192         };
17193
17194         var i = 0;
17195         
17196         for(; i < startingPos; i++) {
17197             textEls[i].innerHTML = (++prevStart);
17198             d.setDate(d.getDate()+1);
17199             
17200             cells[i].className = "fc-past fc-other-month";
17201             setCellClass(this, cells[i]);
17202         }
17203         
17204         var intDay = 0;
17205         
17206         for(; i < days; i++){
17207             intDay = i - startingPos + 1;
17208             textEls[i].innerHTML = (intDay);
17209             d.setDate(d.getDate()+1);
17210             
17211             cells[i].className = ''; // "x-date-active";
17212             setCellClass(this, cells[i]);
17213         }
17214         var extraDays = 0;
17215         
17216         for(; i < 42; i++) {
17217             textEls[i].innerHTML = (++extraDays);
17218             d.setDate(d.getDate()+1);
17219             
17220             cells[i].className = "fc-future fc-other-month";
17221             setCellClass(this, cells[i]);
17222         }
17223         
17224         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17225         
17226         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17227         
17228         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17229         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17230         
17231         if(totalRows != 6){
17232             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17233             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17234         }
17235         
17236         this.fireEvent('monthchange', this, date);
17237         
17238         
17239         /*
17240         if(!this.internalRender){
17241             var main = this.el.dom.firstChild;
17242             var w = main.offsetWidth;
17243             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17244             Roo.fly(main).setWidth(w);
17245             this.internalRender = true;
17246             // opera does not respect the auto grow header center column
17247             // then, after it gets a width opera refuses to recalculate
17248             // without a second pass
17249             if(Roo.isOpera && !this.secondPass){
17250                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17251                 this.secondPass = true;
17252                 this.update.defer(10, this, [date]);
17253             }
17254         }
17255         */
17256         
17257     },
17258     
17259     findCell : function(dt) {
17260         dt = dt.clearTime().getTime();
17261         var ret = false;
17262         this.cells.each(function(c){
17263             //Roo.log("check " +c.dateValue + '?=' + dt);
17264             if(c.dateValue == dt){
17265                 ret = c;
17266                 return false;
17267             }
17268             return true;
17269         });
17270         
17271         return ret;
17272     },
17273     
17274     findCells : function(ev) {
17275         var s = ev.start.clone().clearTime().getTime();
17276        // Roo.log(s);
17277         var e= ev.end.clone().clearTime().getTime();
17278        // Roo.log(e);
17279         var ret = [];
17280         this.cells.each(function(c){
17281              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17282             
17283             if(c.dateValue > e){
17284                 return ;
17285             }
17286             if(c.dateValue < s){
17287                 return ;
17288             }
17289             ret.push(c);
17290         });
17291         
17292         return ret;    
17293     },
17294     
17295 //    findBestRow: function(cells)
17296 //    {
17297 //        var ret = 0;
17298 //        
17299 //        for (var i =0 ; i < cells.length;i++) {
17300 //            ret  = Math.max(cells[i].rows || 0,ret);
17301 //        }
17302 //        return ret;
17303 //        
17304 //    },
17305     
17306     
17307     addItem : function(ev)
17308     {
17309         // look for vertical location slot in
17310         var cells = this.findCells(ev);
17311         
17312 //        ev.row = this.findBestRow(cells);
17313         
17314         // work out the location.
17315         
17316         var crow = false;
17317         var rows = [];
17318         for(var i =0; i < cells.length; i++) {
17319             
17320             cells[i].row = cells[0].row;
17321             
17322             if(i == 0){
17323                 cells[i].row = cells[i].row + 1;
17324             }
17325             
17326             if (!crow) {
17327                 crow = {
17328                     start : cells[i],
17329                     end :  cells[i]
17330                 };
17331                 continue;
17332             }
17333             if (crow.start.getY() == cells[i].getY()) {
17334                 // on same row.
17335                 crow.end = cells[i];
17336                 continue;
17337             }
17338             // different row.
17339             rows.push(crow);
17340             crow = {
17341                 start: cells[i],
17342                 end : cells[i]
17343             };
17344             
17345         }
17346         
17347         rows.push(crow);
17348         ev.els = [];
17349         ev.rows = rows;
17350         ev.cells = cells;
17351         
17352         cells[0].events.push(ev);
17353         
17354         this.calevents.push(ev);
17355     },
17356     
17357     clearEvents: function() {
17358         
17359         if(!this.calevents){
17360             return;
17361         }
17362         
17363         Roo.each(this.cells.elements, function(c){
17364             c.row = 0;
17365             c.events = [];
17366             c.more = [];
17367         });
17368         
17369         Roo.each(this.calevents, function(e) {
17370             Roo.each(e.els, function(el) {
17371                 el.un('mouseenter' ,this.onEventEnter, this);
17372                 el.un('mouseleave' ,this.onEventLeave, this);
17373                 el.remove();
17374             },this);
17375         },this);
17376         
17377         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17378             e.remove();
17379         });
17380         
17381     },
17382     
17383     renderEvents: function()
17384     {   
17385         var _this = this;
17386         
17387         this.cells.each(function(c) {
17388             
17389             if(c.row < 5){
17390                 return;
17391             }
17392             
17393             var ev = c.events;
17394             
17395             var r = 4;
17396             if(c.row != c.events.length){
17397                 r = 4 - (4 - (c.row - c.events.length));
17398             }
17399             
17400             c.events = ev.slice(0, r);
17401             c.more = ev.slice(r);
17402             
17403             if(c.more.length && c.more.length == 1){
17404                 c.events.push(c.more.pop());
17405             }
17406             
17407             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17408             
17409         });
17410             
17411         this.cells.each(function(c) {
17412             
17413             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17414             
17415             
17416             for (var e = 0; e < c.events.length; e++){
17417                 var ev = c.events[e];
17418                 var rows = ev.rows;
17419                 
17420                 for(var i = 0; i < rows.length; i++) {
17421                 
17422                     // how many rows should it span..
17423
17424                     var  cfg = {
17425                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17426                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17427
17428                         unselectable : "on",
17429                         cn : [
17430                             {
17431                                 cls: 'fc-event-inner',
17432                                 cn : [
17433     //                                {
17434     //                                  tag:'span',
17435     //                                  cls: 'fc-event-time',
17436     //                                  html : cells.length > 1 ? '' : ev.time
17437     //                                },
17438                                     {
17439                                       tag:'span',
17440                                       cls: 'fc-event-title',
17441                                       html : String.format('{0}', ev.title)
17442                                     }
17443
17444
17445                                 ]
17446                             },
17447                             {
17448                                 cls: 'ui-resizable-handle ui-resizable-e',
17449                                 html : '&nbsp;&nbsp;&nbsp'
17450                             }
17451
17452                         ]
17453                     };
17454
17455                     if (i == 0) {
17456                         cfg.cls += ' fc-event-start';
17457                     }
17458                     if ((i+1) == rows.length) {
17459                         cfg.cls += ' fc-event-end';
17460                     }
17461
17462                     var ctr = _this.el.select('.fc-event-container',true).first();
17463                     var cg = ctr.createChild(cfg);
17464
17465                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17466                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17467
17468                     var r = (c.more.length) ? 1 : 0;
17469                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17470                     cg.setWidth(ebox.right - sbox.x -2);
17471
17472                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17473                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17474                     cg.on('click', _this.onEventClick, _this, ev);
17475
17476                     ev.els.push(cg);
17477                     
17478                 }
17479                 
17480             }
17481             
17482             
17483             if(c.more.length){
17484                 var  cfg = {
17485                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17486                     style : 'position: absolute',
17487                     unselectable : "on",
17488                     cn : [
17489                         {
17490                             cls: 'fc-event-inner',
17491                             cn : [
17492                                 {
17493                                   tag:'span',
17494                                   cls: 'fc-event-title',
17495                                   html : 'More'
17496                                 }
17497
17498
17499                             ]
17500                         },
17501                         {
17502                             cls: 'ui-resizable-handle ui-resizable-e',
17503                             html : '&nbsp;&nbsp;&nbsp'
17504                         }
17505
17506                     ]
17507                 };
17508
17509                 var ctr = _this.el.select('.fc-event-container',true).first();
17510                 var cg = ctr.createChild(cfg);
17511
17512                 var sbox = c.select('.fc-day-content',true).first().getBox();
17513                 var ebox = c.select('.fc-day-content',true).first().getBox();
17514                 //Roo.log(cg);
17515                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17516                 cg.setWidth(ebox.right - sbox.x -2);
17517
17518                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17519                 
17520             }
17521             
17522         });
17523         
17524         
17525         
17526     },
17527     
17528     onEventEnter: function (e, el,event,d) {
17529         this.fireEvent('evententer', this, el, event);
17530     },
17531     
17532     onEventLeave: function (e, el,event,d) {
17533         this.fireEvent('eventleave', this, el, event);
17534     },
17535     
17536     onEventClick: function (e, el,event,d) {
17537         this.fireEvent('eventclick', this, el, event);
17538     },
17539     
17540     onMonthChange: function () {
17541         this.store.load();
17542     },
17543     
17544     onMoreEventClick: function(e, el, more)
17545     {
17546         var _this = this;
17547         
17548         this.calpopover.placement = 'right';
17549         this.calpopover.setTitle('More');
17550         
17551         this.calpopover.setContent('');
17552         
17553         var ctr = this.calpopover.el.select('.popover-content', true).first();
17554         
17555         Roo.each(more, function(m){
17556             var cfg = {
17557                 cls : 'fc-event-hori fc-event-draggable',
17558                 html : m.title
17559             };
17560             var cg = ctr.createChild(cfg);
17561             
17562             cg.on('click', _this.onEventClick, _this, m);
17563         });
17564         
17565         this.calpopover.show(el);
17566         
17567         
17568     },
17569     
17570     onLoad: function () 
17571     {   
17572         this.calevents = [];
17573         var cal = this;
17574         
17575         if(this.store.getCount() > 0){
17576             this.store.data.each(function(d){
17577                cal.addItem({
17578                     id : d.data.id,
17579                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17580                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17581                     time : d.data.start_time,
17582                     title : d.data.title,
17583                     description : d.data.description,
17584                     venue : d.data.venue
17585                 });
17586             });
17587         }
17588         
17589         this.renderEvents();
17590         
17591         if(this.calevents.length && this.loadMask){
17592             this.maskEl.hide();
17593         }
17594     },
17595     
17596     onBeforeLoad: function()
17597     {
17598         this.clearEvents();
17599         if(this.loadMask){
17600             this.maskEl.show();
17601         }
17602     }
17603 });
17604
17605  
17606  /*
17607  * - LGPL
17608  *
17609  * element
17610  * 
17611  */
17612
17613 /**
17614  * @class Roo.bootstrap.Popover
17615  * @extends Roo.bootstrap.Component
17616  * Bootstrap Popover class
17617  * @cfg {String} html contents of the popover   (or false to use children..)
17618  * @cfg {String} title of popover (or false to hide)
17619  * @cfg {String} placement how it is placed
17620  * @cfg {String} trigger click || hover (or false to trigger manually)
17621  * @cfg {String} over what (parent or false to trigger manually.)
17622  * @cfg {Number} delay - delay before showing
17623  
17624  * @constructor
17625  * Create a new Popover
17626  * @param {Object} config The config object
17627  */
17628
17629 Roo.bootstrap.Popover = function(config){
17630     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17631     
17632     this.addEvents({
17633         // raw events
17634          /**
17635          * @event show
17636          * After the popover show
17637          * 
17638          * @param {Roo.bootstrap.Popover} this
17639          */
17640         "show" : true,
17641         /**
17642          * @event hide
17643          * After the popover hide
17644          * 
17645          * @param {Roo.bootstrap.Popover} this
17646          */
17647         "hide" : true
17648     });
17649 };
17650
17651 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17652     
17653     title: 'Fill in a title',
17654     html: false,
17655     
17656     placement : 'right',
17657     trigger : 'hover', // hover
17658     
17659     delay : 0,
17660     
17661     over: 'parent',
17662     
17663     can_build_overlaid : false,
17664     
17665     getChildContainer : function()
17666     {
17667         return this.el.select('.popover-content',true).first();
17668     },
17669     
17670     getAutoCreate : function(){
17671          
17672         var cfg = {
17673            cls : 'popover roo-dynamic',
17674            style: 'display:block',
17675            cn : [
17676                 {
17677                     cls : 'arrow'
17678                 },
17679                 {
17680                     cls : 'popover-inner',
17681                     cn : [
17682                         {
17683                             tag: 'h3',
17684                             cls: 'popover-title popover-header',
17685                             html : this.title
17686                         },
17687                         {
17688                             cls : 'popover-content popover-body',
17689                             html : this.html
17690                         }
17691                     ]
17692                     
17693                 }
17694            ]
17695         };
17696         
17697         return cfg;
17698     },
17699     setTitle: function(str)
17700     {
17701         this.title = str;
17702         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17703     },
17704     setContent: function(str)
17705     {
17706         this.html = str;
17707         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17708     },
17709     // as it get's added to the bottom of the page.
17710     onRender : function(ct, position)
17711     {
17712         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17713         if(!this.el){
17714             var cfg = Roo.apply({},  this.getAutoCreate());
17715             cfg.id = Roo.id();
17716             
17717             if (this.cls) {
17718                 cfg.cls += ' ' + this.cls;
17719             }
17720             if (this.style) {
17721                 cfg.style = this.style;
17722             }
17723             //Roo.log("adding to ");
17724             this.el = Roo.get(document.body).createChild(cfg, position);
17725 //            Roo.log(this.el);
17726         }
17727         this.initEvents();
17728     },
17729     
17730     initEvents : function()
17731     {
17732         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17733         this.el.enableDisplayMode('block');
17734         this.el.hide();
17735         if (this.over === false) {
17736             return; 
17737         }
17738         if (this.triggers === false) {
17739             return;
17740         }
17741         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17742         var triggers = this.trigger ? this.trigger.split(' ') : [];
17743         Roo.each(triggers, function(trigger) {
17744         
17745             if (trigger == 'click') {
17746                 on_el.on('click', this.toggle, this);
17747             } else if (trigger != 'manual') {
17748                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17749                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17750       
17751                 on_el.on(eventIn  ,this.enter, this);
17752                 on_el.on(eventOut, this.leave, this);
17753             }
17754         }, this);
17755         
17756     },
17757     
17758     
17759     // private
17760     timeout : null,
17761     hoverState : null,
17762     
17763     toggle : function () {
17764         this.hoverState == 'in' ? this.leave() : this.enter();
17765     },
17766     
17767     enter : function () {
17768         
17769         clearTimeout(this.timeout);
17770     
17771         this.hoverState = 'in';
17772     
17773         if (!this.delay || !this.delay.show) {
17774             this.show();
17775             return;
17776         }
17777         var _t = this;
17778         this.timeout = setTimeout(function () {
17779             if (_t.hoverState == 'in') {
17780                 _t.show();
17781             }
17782         }, this.delay.show)
17783     },
17784     
17785     leave : function() {
17786         clearTimeout(this.timeout);
17787     
17788         this.hoverState = 'out';
17789     
17790         if (!this.delay || !this.delay.hide) {
17791             this.hide();
17792             return;
17793         }
17794         var _t = this;
17795         this.timeout = setTimeout(function () {
17796             if (_t.hoverState == 'out') {
17797                 _t.hide();
17798             }
17799         }, this.delay.hide)
17800     },
17801     
17802     show : function (on_el)
17803     {
17804         if (!on_el) {
17805             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17806         }
17807         
17808         // set content.
17809         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17810         if (this.html !== false) {
17811             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17812         }
17813         this.el.removeClass([
17814             'fade','top','bottom', 'left', 'right','in',
17815             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17816         ]);
17817         if (!this.title.length) {
17818             this.el.select('.popover-title',true).hide();
17819         }
17820         
17821         var placement = typeof this.placement == 'function' ?
17822             this.placement.call(this, this.el, on_el) :
17823             this.placement;
17824             
17825         var autoToken = /\s?auto?\s?/i;
17826         var autoPlace = autoToken.test(placement);
17827         if (autoPlace) {
17828             placement = placement.replace(autoToken, '') || 'top';
17829         }
17830         
17831         //this.el.detach()
17832         //this.el.setXY([0,0]);
17833         this.el.show();
17834         this.el.dom.style.display='block';
17835         this.el.addClass(placement);
17836         
17837         //this.el.appendTo(on_el);
17838         
17839         var p = this.getPosition();
17840         var box = this.el.getBox();
17841         
17842         if (autoPlace) {
17843             // fixme..
17844         }
17845         var align = Roo.bootstrap.Popover.alignment[placement];
17846         
17847 //        Roo.log(align);
17848         this.el.alignTo(on_el, align[0],align[1]);
17849         //var arrow = this.el.select('.arrow',true).first();
17850         //arrow.set(align[2], 
17851         
17852         this.el.addClass('in');
17853         
17854         
17855         if (this.el.hasClass('fade')) {
17856             // fade it?
17857         }
17858         
17859         this.hoverState = 'in';
17860         
17861         this.fireEvent('show', this);
17862         
17863     },
17864     hide : function()
17865     {
17866         this.el.setXY([0,0]);
17867         this.el.removeClass('in');
17868         this.el.hide();
17869         this.hoverState = null;
17870         
17871         this.fireEvent('hide', this);
17872     }
17873     
17874 });
17875
17876 Roo.bootstrap.Popover.alignment = {
17877     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17878     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17879     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17880     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17881 };
17882
17883  /*
17884  * - LGPL
17885  *
17886  * Progress
17887  * 
17888  */
17889
17890 /**
17891  * @class Roo.bootstrap.Progress
17892  * @extends Roo.bootstrap.Component
17893  * Bootstrap Progress class
17894  * @cfg {Boolean} striped striped of the progress bar
17895  * @cfg {Boolean} active animated of the progress bar
17896  * 
17897  * 
17898  * @constructor
17899  * Create a new Progress
17900  * @param {Object} config The config object
17901  */
17902
17903 Roo.bootstrap.Progress = function(config){
17904     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17905 };
17906
17907 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17908     
17909     striped : false,
17910     active: false,
17911     
17912     getAutoCreate : function(){
17913         var cfg = {
17914             tag: 'div',
17915             cls: 'progress'
17916         };
17917         
17918         
17919         if(this.striped){
17920             cfg.cls += ' progress-striped';
17921         }
17922       
17923         if(this.active){
17924             cfg.cls += ' active';
17925         }
17926         
17927         
17928         return cfg;
17929     }
17930    
17931 });
17932
17933  
17934
17935  /*
17936  * - LGPL
17937  *
17938  * ProgressBar
17939  * 
17940  */
17941
17942 /**
17943  * @class Roo.bootstrap.ProgressBar
17944  * @extends Roo.bootstrap.Component
17945  * Bootstrap ProgressBar class
17946  * @cfg {Number} aria_valuenow aria-value now
17947  * @cfg {Number} aria_valuemin aria-value min
17948  * @cfg {Number} aria_valuemax aria-value max
17949  * @cfg {String} label label for the progress bar
17950  * @cfg {String} panel (success | info | warning | danger )
17951  * @cfg {String} role role of the progress bar
17952  * @cfg {String} sr_only text
17953  * 
17954  * 
17955  * @constructor
17956  * Create a new ProgressBar
17957  * @param {Object} config The config object
17958  */
17959
17960 Roo.bootstrap.ProgressBar = function(config){
17961     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17962 };
17963
17964 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17965     
17966     aria_valuenow : 0,
17967     aria_valuemin : 0,
17968     aria_valuemax : 100,
17969     label : false,
17970     panel : false,
17971     role : false,
17972     sr_only: false,
17973     
17974     getAutoCreate : function()
17975     {
17976         
17977         var cfg = {
17978             tag: 'div',
17979             cls: 'progress-bar',
17980             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17981         };
17982         
17983         if(this.sr_only){
17984             cfg.cn = {
17985                 tag: 'span',
17986                 cls: 'sr-only',
17987                 html: this.sr_only
17988             }
17989         }
17990         
17991         if(this.role){
17992             cfg.role = this.role;
17993         }
17994         
17995         if(this.aria_valuenow){
17996             cfg['aria-valuenow'] = this.aria_valuenow;
17997         }
17998         
17999         if(this.aria_valuemin){
18000             cfg['aria-valuemin'] = this.aria_valuemin;
18001         }
18002         
18003         if(this.aria_valuemax){
18004             cfg['aria-valuemax'] = this.aria_valuemax;
18005         }
18006         
18007         if(this.label && !this.sr_only){
18008             cfg.html = this.label;
18009         }
18010         
18011         if(this.panel){
18012             cfg.cls += ' progress-bar-' + this.panel;
18013         }
18014         
18015         return cfg;
18016     },
18017     
18018     update : function(aria_valuenow)
18019     {
18020         this.aria_valuenow = aria_valuenow;
18021         
18022         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18023     }
18024    
18025 });
18026
18027  
18028
18029  /*
18030  * - LGPL
18031  *
18032  * column
18033  * 
18034  */
18035
18036 /**
18037  * @class Roo.bootstrap.TabGroup
18038  * @extends Roo.bootstrap.Column
18039  * Bootstrap Column class
18040  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18041  * @cfg {Boolean} carousel true to make the group behave like a carousel
18042  * @cfg {Boolean} bullets show bullets for the panels
18043  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18044  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18045  * @cfg {Boolean} showarrow (true|false) show arrow default true
18046  * 
18047  * @constructor
18048  * Create a new TabGroup
18049  * @param {Object} config The config object
18050  */
18051
18052 Roo.bootstrap.TabGroup = function(config){
18053     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18054     if (!this.navId) {
18055         this.navId = Roo.id();
18056     }
18057     this.tabs = [];
18058     Roo.bootstrap.TabGroup.register(this);
18059     
18060 };
18061
18062 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18063     
18064     carousel : false,
18065     transition : false,
18066     bullets : 0,
18067     timer : 0,
18068     autoslide : false,
18069     slideFn : false,
18070     slideOnTouch : false,
18071     showarrow : true,
18072     
18073     getAutoCreate : function()
18074     {
18075         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18076         
18077         cfg.cls += ' tab-content';
18078         
18079         if (this.carousel) {
18080             cfg.cls += ' carousel slide';
18081             
18082             cfg.cn = [{
18083                cls : 'carousel-inner',
18084                cn : []
18085             }];
18086         
18087             if(this.bullets  && !Roo.isTouch){
18088                 
18089                 var bullets = {
18090                     cls : 'carousel-bullets',
18091                     cn : []
18092                 };
18093                
18094                 if(this.bullets_cls){
18095                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18096                 }
18097                 
18098                 bullets.cn.push({
18099                     cls : 'clear'
18100                 });
18101                 
18102                 cfg.cn[0].cn.push(bullets);
18103             }
18104             
18105             if(this.showarrow){
18106                 cfg.cn[0].cn.push({
18107                     tag : 'div',
18108                     class : 'carousel-arrow',
18109                     cn : [
18110                         {
18111                             tag : 'div',
18112                             class : 'carousel-prev',
18113                             cn : [
18114                                 {
18115                                     tag : 'i',
18116                                     class : 'fa fa-chevron-left'
18117                                 }
18118                             ]
18119                         },
18120                         {
18121                             tag : 'div',
18122                             class : 'carousel-next',
18123                             cn : [
18124                                 {
18125                                     tag : 'i',
18126                                     class : 'fa fa-chevron-right'
18127                                 }
18128                             ]
18129                         }
18130                     ]
18131                 });
18132             }
18133             
18134         }
18135         
18136         return cfg;
18137     },
18138     
18139     initEvents:  function()
18140     {
18141 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18142 //            this.el.on("touchstart", this.onTouchStart, this);
18143 //        }
18144         
18145         if(this.autoslide){
18146             var _this = this;
18147             
18148             this.slideFn = window.setInterval(function() {
18149                 _this.showPanelNext();
18150             }, this.timer);
18151         }
18152         
18153         if(this.showarrow){
18154             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18155             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18156         }
18157         
18158         
18159     },
18160     
18161 //    onTouchStart : function(e, el, o)
18162 //    {
18163 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18164 //            return;
18165 //        }
18166 //        
18167 //        this.showPanelNext();
18168 //    },
18169     
18170     
18171     getChildContainer : function()
18172     {
18173         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18174     },
18175     
18176     /**
18177     * register a Navigation item
18178     * @param {Roo.bootstrap.NavItem} the navitem to add
18179     */
18180     register : function(item)
18181     {
18182         this.tabs.push( item);
18183         item.navId = this.navId; // not really needed..
18184         this.addBullet();
18185     
18186     },
18187     
18188     getActivePanel : function()
18189     {
18190         var r = false;
18191         Roo.each(this.tabs, function(t) {
18192             if (t.active) {
18193                 r = t;
18194                 return false;
18195             }
18196             return null;
18197         });
18198         return r;
18199         
18200     },
18201     getPanelByName : function(n)
18202     {
18203         var r = false;
18204         Roo.each(this.tabs, function(t) {
18205             if (t.tabId == n) {
18206                 r = t;
18207                 return false;
18208             }
18209             return null;
18210         });
18211         return r;
18212     },
18213     indexOfPanel : function(p)
18214     {
18215         var r = false;
18216         Roo.each(this.tabs, function(t,i) {
18217             if (t.tabId == p.tabId) {
18218                 r = i;
18219                 return false;
18220             }
18221             return null;
18222         });
18223         return r;
18224     },
18225     /**
18226      * show a specific panel
18227      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18228      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18229      */
18230     showPanel : function (pan)
18231     {
18232         if(this.transition || typeof(pan) == 'undefined'){
18233             Roo.log("waiting for the transitionend");
18234             return;
18235         }
18236         
18237         if (typeof(pan) == 'number') {
18238             pan = this.tabs[pan];
18239         }
18240         
18241         if (typeof(pan) == 'string') {
18242             pan = this.getPanelByName(pan);
18243         }
18244         
18245         var cur = this.getActivePanel();
18246         
18247         if(!pan || !cur){
18248             Roo.log('pan or acitve pan is undefined');
18249             return false;
18250         }
18251         
18252         if (pan.tabId == this.getActivePanel().tabId) {
18253             return true;
18254         }
18255         
18256         if (false === cur.fireEvent('beforedeactivate')) {
18257             return false;
18258         }
18259         
18260         if(this.bullets > 0 && !Roo.isTouch){
18261             this.setActiveBullet(this.indexOfPanel(pan));
18262         }
18263         
18264         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18265             
18266             this.transition = true;
18267             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18268             var lr = dir == 'next' ? 'left' : 'right';
18269             pan.el.addClass(dir); // or prev
18270             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18271             cur.el.addClass(lr); // or right
18272             pan.el.addClass(lr);
18273             
18274             var _this = this;
18275             cur.el.on('transitionend', function() {
18276                 Roo.log("trans end?");
18277                 
18278                 pan.el.removeClass([lr,dir]);
18279                 pan.setActive(true);
18280                 
18281                 cur.el.removeClass([lr]);
18282                 cur.setActive(false);
18283                 
18284                 _this.transition = false;
18285                 
18286             }, this, { single:  true } );
18287             
18288             return true;
18289         }
18290         
18291         cur.setActive(false);
18292         pan.setActive(true);
18293         
18294         return true;
18295         
18296     },
18297     showPanelNext : function()
18298     {
18299         var i = this.indexOfPanel(this.getActivePanel());
18300         
18301         if (i >= this.tabs.length - 1 && !this.autoslide) {
18302             return;
18303         }
18304         
18305         if (i >= this.tabs.length - 1 && this.autoslide) {
18306             i = -1;
18307         }
18308         
18309         this.showPanel(this.tabs[i+1]);
18310     },
18311     
18312     showPanelPrev : function()
18313     {
18314         var i = this.indexOfPanel(this.getActivePanel());
18315         
18316         if (i  < 1 && !this.autoslide) {
18317             return;
18318         }
18319         
18320         if (i < 1 && this.autoslide) {
18321             i = this.tabs.length;
18322         }
18323         
18324         this.showPanel(this.tabs[i-1]);
18325     },
18326     
18327     
18328     addBullet: function()
18329     {
18330         if(!this.bullets || Roo.isTouch){
18331             return;
18332         }
18333         var ctr = this.el.select('.carousel-bullets',true).first();
18334         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18335         var bullet = ctr.createChild({
18336             cls : 'bullet bullet-' + i
18337         },ctr.dom.lastChild);
18338         
18339         
18340         var _this = this;
18341         
18342         bullet.on('click', (function(e, el, o, ii, t){
18343
18344             e.preventDefault();
18345
18346             this.showPanel(ii);
18347
18348             if(this.autoslide && this.slideFn){
18349                 clearInterval(this.slideFn);
18350                 this.slideFn = window.setInterval(function() {
18351                     _this.showPanelNext();
18352                 }, this.timer);
18353             }
18354
18355         }).createDelegate(this, [i, bullet], true));
18356                 
18357         
18358     },
18359      
18360     setActiveBullet : function(i)
18361     {
18362         if(Roo.isTouch){
18363             return;
18364         }
18365         
18366         Roo.each(this.el.select('.bullet', true).elements, function(el){
18367             el.removeClass('selected');
18368         });
18369
18370         var bullet = this.el.select('.bullet-' + i, true).first();
18371         
18372         if(!bullet){
18373             return;
18374         }
18375         
18376         bullet.addClass('selected');
18377     }
18378     
18379     
18380   
18381 });
18382
18383  
18384
18385  
18386  
18387 Roo.apply(Roo.bootstrap.TabGroup, {
18388     
18389     groups: {},
18390      /**
18391     * register a Navigation Group
18392     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18393     */
18394     register : function(navgrp)
18395     {
18396         this.groups[navgrp.navId] = navgrp;
18397         
18398     },
18399     /**
18400     * fetch a Navigation Group based on the navigation ID
18401     * if one does not exist , it will get created.
18402     * @param {string} the navgroup to add
18403     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18404     */
18405     get: function(navId) {
18406         if (typeof(this.groups[navId]) == 'undefined') {
18407             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18408         }
18409         return this.groups[navId] ;
18410     }
18411     
18412     
18413     
18414 });
18415
18416  /*
18417  * - LGPL
18418  *
18419  * TabPanel
18420  * 
18421  */
18422
18423 /**
18424  * @class Roo.bootstrap.TabPanel
18425  * @extends Roo.bootstrap.Component
18426  * Bootstrap TabPanel class
18427  * @cfg {Boolean} active panel active
18428  * @cfg {String} html panel content
18429  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18430  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18431  * @cfg {String} href click to link..
18432  * 
18433  * 
18434  * @constructor
18435  * Create a new TabPanel
18436  * @param {Object} config The config object
18437  */
18438
18439 Roo.bootstrap.TabPanel = function(config){
18440     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18441     this.addEvents({
18442         /**
18443              * @event changed
18444              * Fires when the active status changes
18445              * @param {Roo.bootstrap.TabPanel} this
18446              * @param {Boolean} state the new state
18447             
18448          */
18449         'changed': true,
18450         /**
18451              * @event beforedeactivate
18452              * Fires before a tab is de-activated - can be used to do validation on a form.
18453              * @param {Roo.bootstrap.TabPanel} this
18454              * @return {Boolean} false if there is an error
18455             
18456          */
18457         'beforedeactivate': true
18458      });
18459     
18460     this.tabId = this.tabId || Roo.id();
18461   
18462 };
18463
18464 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18465     
18466     active: false,
18467     html: false,
18468     tabId: false,
18469     navId : false,
18470     href : '',
18471     
18472     getAutoCreate : function(){
18473         var cfg = {
18474             tag: 'div',
18475             // item is needed for carousel - not sure if it has any effect otherwise
18476             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18477             html: this.html || ''
18478         };
18479         
18480         if(this.active){
18481             cfg.cls += ' active';
18482         }
18483         
18484         if(this.tabId){
18485             cfg.tabId = this.tabId;
18486         }
18487         
18488         
18489         return cfg;
18490     },
18491     
18492     initEvents:  function()
18493     {
18494         var p = this.parent();
18495         
18496         this.navId = this.navId || p.navId;
18497         
18498         if (typeof(this.navId) != 'undefined') {
18499             // not really needed.. but just in case.. parent should be a NavGroup.
18500             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18501             
18502             tg.register(this);
18503             
18504             var i = tg.tabs.length - 1;
18505             
18506             if(this.active && tg.bullets > 0 && i < tg.bullets){
18507                 tg.setActiveBullet(i);
18508             }
18509         }
18510         
18511         this.el.on('click', this.onClick, this);
18512         
18513         if(Roo.isTouch){
18514             this.el.on("touchstart", this.onTouchStart, this);
18515             this.el.on("touchmove", this.onTouchMove, this);
18516             this.el.on("touchend", this.onTouchEnd, this);
18517         }
18518         
18519     },
18520     
18521     onRender : function(ct, position)
18522     {
18523         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18524     },
18525     
18526     setActive : function(state)
18527     {
18528         Roo.log("panel - set active " + this.tabId + "=" + state);
18529         
18530         this.active = state;
18531         if (!state) {
18532             this.el.removeClass('active');
18533             
18534         } else  if (!this.el.hasClass('active')) {
18535             this.el.addClass('active');
18536         }
18537         
18538         this.fireEvent('changed', this, state);
18539     },
18540     
18541     onClick : function(e)
18542     {
18543         e.preventDefault();
18544         
18545         if(!this.href.length){
18546             return;
18547         }
18548         
18549         window.location.href = this.href;
18550     },
18551     
18552     startX : 0,
18553     startY : 0,
18554     endX : 0,
18555     endY : 0,
18556     swiping : false,
18557     
18558     onTouchStart : function(e)
18559     {
18560         this.swiping = false;
18561         
18562         this.startX = e.browserEvent.touches[0].clientX;
18563         this.startY = e.browserEvent.touches[0].clientY;
18564     },
18565     
18566     onTouchMove : function(e)
18567     {
18568         this.swiping = true;
18569         
18570         this.endX = e.browserEvent.touches[0].clientX;
18571         this.endY = e.browserEvent.touches[0].clientY;
18572     },
18573     
18574     onTouchEnd : function(e)
18575     {
18576         if(!this.swiping){
18577             this.onClick(e);
18578             return;
18579         }
18580         
18581         var tabGroup = this.parent();
18582         
18583         if(this.endX > this.startX){ // swiping right
18584             tabGroup.showPanelPrev();
18585             return;
18586         }
18587         
18588         if(this.startX > this.endX){ // swiping left
18589             tabGroup.showPanelNext();
18590             return;
18591         }
18592     }
18593     
18594     
18595 });
18596  
18597
18598  
18599
18600  /*
18601  * - LGPL
18602  *
18603  * DateField
18604  * 
18605  */
18606
18607 /**
18608  * @class Roo.bootstrap.DateField
18609  * @extends Roo.bootstrap.Input
18610  * Bootstrap DateField class
18611  * @cfg {Number} weekStart default 0
18612  * @cfg {String} viewMode default empty, (months|years)
18613  * @cfg {String} minViewMode default empty, (months|years)
18614  * @cfg {Number} startDate default -Infinity
18615  * @cfg {Number} endDate default Infinity
18616  * @cfg {Boolean} todayHighlight default false
18617  * @cfg {Boolean} todayBtn default false
18618  * @cfg {Boolean} calendarWeeks default false
18619  * @cfg {Object} daysOfWeekDisabled default empty
18620  * @cfg {Boolean} singleMode default false (true | false)
18621  * 
18622  * @cfg {Boolean} keyboardNavigation default true
18623  * @cfg {String} language default en
18624  * 
18625  * @constructor
18626  * Create a new DateField
18627  * @param {Object} config The config object
18628  */
18629
18630 Roo.bootstrap.DateField = function(config){
18631     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18632      this.addEvents({
18633             /**
18634              * @event show
18635              * Fires when this field show.
18636              * @param {Roo.bootstrap.DateField} this
18637              * @param {Mixed} date The date value
18638              */
18639             show : true,
18640             /**
18641              * @event show
18642              * Fires when this field hide.
18643              * @param {Roo.bootstrap.DateField} this
18644              * @param {Mixed} date The date value
18645              */
18646             hide : true,
18647             /**
18648              * @event select
18649              * Fires when select a date.
18650              * @param {Roo.bootstrap.DateField} this
18651              * @param {Mixed} date The date value
18652              */
18653             select : true,
18654             /**
18655              * @event beforeselect
18656              * Fires when before select a date.
18657              * @param {Roo.bootstrap.DateField} this
18658              * @param {Mixed} date The date value
18659              */
18660             beforeselect : true
18661         });
18662 };
18663
18664 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18665     
18666     /**
18667      * @cfg {String} format
18668      * The default date format string which can be overriden for localization support.  The format must be
18669      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18670      */
18671     format : "m/d/y",
18672     /**
18673      * @cfg {String} altFormats
18674      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18675      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18676      */
18677     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18678     
18679     weekStart : 0,
18680     
18681     viewMode : '',
18682     
18683     minViewMode : '',
18684     
18685     todayHighlight : false,
18686     
18687     todayBtn: false,
18688     
18689     language: 'en',
18690     
18691     keyboardNavigation: true,
18692     
18693     calendarWeeks: false,
18694     
18695     startDate: -Infinity,
18696     
18697     endDate: Infinity,
18698     
18699     daysOfWeekDisabled: [],
18700     
18701     _events: [],
18702     
18703     singleMode : false,
18704     
18705     UTCDate: function()
18706     {
18707         return new Date(Date.UTC.apply(Date, arguments));
18708     },
18709     
18710     UTCToday: function()
18711     {
18712         var today = new Date();
18713         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18714     },
18715     
18716     getDate: function() {
18717             var d = this.getUTCDate();
18718             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18719     },
18720     
18721     getUTCDate: function() {
18722             return this.date;
18723     },
18724     
18725     setDate: function(d) {
18726             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18727     },
18728     
18729     setUTCDate: function(d) {
18730             this.date = d;
18731             this.setValue(this.formatDate(this.date));
18732     },
18733         
18734     onRender: function(ct, position)
18735     {
18736         
18737         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18738         
18739         this.language = this.language || 'en';
18740         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18741         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18742         
18743         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18744         this.format = this.format || 'm/d/y';
18745         this.isInline = false;
18746         this.isInput = true;
18747         this.component = this.el.select('.add-on', true).first() || false;
18748         this.component = (this.component && this.component.length === 0) ? false : this.component;
18749         this.hasInput = this.component && this.inputEl().length;
18750         
18751         if (typeof(this.minViewMode === 'string')) {
18752             switch (this.minViewMode) {
18753                 case 'months':
18754                     this.minViewMode = 1;
18755                     break;
18756                 case 'years':
18757                     this.minViewMode = 2;
18758                     break;
18759                 default:
18760                     this.minViewMode = 0;
18761                     break;
18762             }
18763         }
18764         
18765         if (typeof(this.viewMode === 'string')) {
18766             switch (this.viewMode) {
18767                 case 'months':
18768                     this.viewMode = 1;
18769                     break;
18770                 case 'years':
18771                     this.viewMode = 2;
18772                     break;
18773                 default:
18774                     this.viewMode = 0;
18775                     break;
18776             }
18777         }
18778                 
18779         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18780         
18781 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18782         
18783         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18784         
18785         this.picker().on('mousedown', this.onMousedown, this);
18786         this.picker().on('click', this.onClick, this);
18787         
18788         this.picker().addClass('datepicker-dropdown');
18789         
18790         this.startViewMode = this.viewMode;
18791         
18792         if(this.singleMode){
18793             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18794                 v.setVisibilityMode(Roo.Element.DISPLAY);
18795                 v.hide();
18796             });
18797             
18798             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18799                 v.setStyle('width', '189px');
18800             });
18801         }
18802         
18803         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18804             if(!this.calendarWeeks){
18805                 v.remove();
18806                 return;
18807             }
18808             
18809             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18810             v.attr('colspan', function(i, val){
18811                 return parseInt(val) + 1;
18812             });
18813         });
18814                         
18815         
18816         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18817         
18818         this.setStartDate(this.startDate);
18819         this.setEndDate(this.endDate);
18820         
18821         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18822         
18823         this.fillDow();
18824         this.fillMonths();
18825         this.update();
18826         this.showMode();
18827         
18828         if(this.isInline) {
18829             this.showPopup();
18830         }
18831     },
18832     
18833     picker : function()
18834     {
18835         return this.pickerEl;
18836 //        return this.el.select('.datepicker', true).first();
18837     },
18838     
18839     fillDow: function()
18840     {
18841         var dowCnt = this.weekStart;
18842         
18843         var dow = {
18844             tag: 'tr',
18845             cn: [
18846                 
18847             ]
18848         };
18849         
18850         if(this.calendarWeeks){
18851             dow.cn.push({
18852                 tag: 'th',
18853                 cls: 'cw',
18854                 html: '&nbsp;'
18855             })
18856         }
18857         
18858         while (dowCnt < this.weekStart + 7) {
18859             dow.cn.push({
18860                 tag: 'th',
18861                 cls: 'dow',
18862                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18863             });
18864         }
18865         
18866         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18867     },
18868     
18869     fillMonths: function()
18870     {    
18871         var i = 0;
18872         var months = this.picker().select('>.datepicker-months td', true).first();
18873         
18874         months.dom.innerHTML = '';
18875         
18876         while (i < 12) {
18877             var month = {
18878                 tag: 'span',
18879                 cls: 'month',
18880                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18881             };
18882             
18883             months.createChild(month);
18884         }
18885         
18886     },
18887     
18888     update: function()
18889     {
18890         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;
18891         
18892         if (this.date < this.startDate) {
18893             this.viewDate = new Date(this.startDate);
18894         } else if (this.date > this.endDate) {
18895             this.viewDate = new Date(this.endDate);
18896         } else {
18897             this.viewDate = new Date(this.date);
18898         }
18899         
18900         this.fill();
18901     },
18902     
18903     fill: function() 
18904     {
18905         var d = new Date(this.viewDate),
18906                 year = d.getUTCFullYear(),
18907                 month = d.getUTCMonth(),
18908                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18909                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18910                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18911                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18912                 currentDate = this.date && this.date.valueOf(),
18913                 today = this.UTCToday();
18914         
18915         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18916         
18917 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18918         
18919 //        this.picker.select('>tfoot th.today').
18920 //                                              .text(dates[this.language].today)
18921 //                                              .toggle(this.todayBtn !== false);
18922     
18923         this.updateNavArrows();
18924         this.fillMonths();
18925                                                 
18926         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18927         
18928         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18929          
18930         prevMonth.setUTCDate(day);
18931         
18932         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18933         
18934         var nextMonth = new Date(prevMonth);
18935         
18936         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18937         
18938         nextMonth = nextMonth.valueOf();
18939         
18940         var fillMonths = false;
18941         
18942         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18943         
18944         while(prevMonth.valueOf() <= nextMonth) {
18945             var clsName = '';
18946             
18947             if (prevMonth.getUTCDay() === this.weekStart) {
18948                 if(fillMonths){
18949                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18950                 }
18951                     
18952                 fillMonths = {
18953                     tag: 'tr',
18954                     cn: []
18955                 };
18956                 
18957                 if(this.calendarWeeks){
18958                     // ISO 8601: First week contains first thursday.
18959                     // ISO also states week starts on Monday, but we can be more abstract here.
18960                     var
18961                     // Start of current week: based on weekstart/current date
18962                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18963                     // Thursday of this week
18964                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18965                     // First Thursday of year, year from thursday
18966                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18967                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18968                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18969                     
18970                     fillMonths.cn.push({
18971                         tag: 'td',
18972                         cls: 'cw',
18973                         html: calWeek
18974                     });
18975                 }
18976             }
18977             
18978             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18979                 clsName += ' old';
18980             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18981                 clsName += ' new';
18982             }
18983             if (this.todayHighlight &&
18984                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18985                 prevMonth.getUTCMonth() == today.getMonth() &&
18986                 prevMonth.getUTCDate() == today.getDate()) {
18987                 clsName += ' today';
18988             }
18989             
18990             if (currentDate && prevMonth.valueOf() === currentDate) {
18991                 clsName += ' active';
18992             }
18993             
18994             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18995                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18996                     clsName += ' disabled';
18997             }
18998             
18999             fillMonths.cn.push({
19000                 tag: 'td',
19001                 cls: 'day ' + clsName,
19002                 html: prevMonth.getDate()
19003             });
19004             
19005             prevMonth.setDate(prevMonth.getDate()+1);
19006         }
19007           
19008         var currentYear = this.date && this.date.getUTCFullYear();
19009         var currentMonth = this.date && this.date.getUTCMonth();
19010         
19011         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19012         
19013         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19014             v.removeClass('active');
19015             
19016             if(currentYear === year && k === currentMonth){
19017                 v.addClass('active');
19018             }
19019             
19020             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19021                 v.addClass('disabled');
19022             }
19023             
19024         });
19025         
19026         
19027         year = parseInt(year/10, 10) * 10;
19028         
19029         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19030         
19031         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19032         
19033         year -= 1;
19034         for (var i = -1; i < 11; i++) {
19035             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19036                 tag: 'span',
19037                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19038                 html: year
19039             });
19040             
19041             year += 1;
19042         }
19043     },
19044     
19045     showMode: function(dir) 
19046     {
19047         if (dir) {
19048             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19049         }
19050         
19051         Roo.each(this.picker().select('>div',true).elements, function(v){
19052             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19053             v.hide();
19054         });
19055         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19056     },
19057     
19058     place: function()
19059     {
19060         if(this.isInline) {
19061             return;
19062         }
19063         
19064         this.picker().removeClass(['bottom', 'top']);
19065         
19066         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19067             /*
19068              * place to the top of element!
19069              *
19070              */
19071             
19072             this.picker().addClass('top');
19073             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19074             
19075             return;
19076         }
19077         
19078         this.picker().addClass('bottom');
19079         
19080         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19081     },
19082     
19083     parseDate : function(value)
19084     {
19085         if(!value || value instanceof Date){
19086             return value;
19087         }
19088         var v = Date.parseDate(value, this.format);
19089         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19090             v = Date.parseDate(value, 'Y-m-d');
19091         }
19092         if(!v && this.altFormats){
19093             if(!this.altFormatsArray){
19094                 this.altFormatsArray = this.altFormats.split("|");
19095             }
19096             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19097                 v = Date.parseDate(value, this.altFormatsArray[i]);
19098             }
19099         }
19100         return v;
19101     },
19102     
19103     formatDate : function(date, fmt)
19104     {   
19105         return (!date || !(date instanceof Date)) ?
19106         date : date.dateFormat(fmt || this.format);
19107     },
19108     
19109     onFocus : function()
19110     {
19111         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19112         this.showPopup();
19113     },
19114     
19115     onBlur : function()
19116     {
19117         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19118         
19119         var d = this.inputEl().getValue();
19120         
19121         this.setValue(d);
19122                 
19123         this.hidePopup();
19124     },
19125     
19126     showPopup : function()
19127     {
19128         this.picker().show();
19129         this.update();
19130         this.place();
19131         
19132         this.fireEvent('showpopup', this, this.date);
19133     },
19134     
19135     hidePopup : function()
19136     {
19137         if(this.isInline) {
19138             return;
19139         }
19140         this.picker().hide();
19141         this.viewMode = this.startViewMode;
19142         this.showMode();
19143         
19144         this.fireEvent('hidepopup', this, this.date);
19145         
19146     },
19147     
19148     onMousedown: function(e)
19149     {
19150         e.stopPropagation();
19151         e.preventDefault();
19152     },
19153     
19154     keyup: function(e)
19155     {
19156         Roo.bootstrap.DateField.superclass.keyup.call(this);
19157         this.update();
19158     },
19159
19160     setValue: function(v)
19161     {
19162         if(this.fireEvent('beforeselect', this, v) !== false){
19163             var d = new Date(this.parseDate(v) ).clearTime();
19164         
19165             if(isNaN(d.getTime())){
19166                 this.date = this.viewDate = '';
19167                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19168                 return;
19169             }
19170
19171             v = this.formatDate(d);
19172
19173             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19174
19175             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19176
19177             this.update();
19178
19179             this.fireEvent('select', this, this.date);
19180         }
19181     },
19182     
19183     getValue: function()
19184     {
19185         return this.formatDate(this.date);
19186     },
19187     
19188     fireKey: function(e)
19189     {
19190         if (!this.picker().isVisible()){
19191             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19192                 this.showPopup();
19193             }
19194             return;
19195         }
19196         
19197         var dateChanged = false,
19198         dir, day, month,
19199         newDate, newViewDate;
19200         
19201         switch(e.keyCode){
19202             case 27: // escape
19203                 this.hidePopup();
19204                 e.preventDefault();
19205                 break;
19206             case 37: // left
19207             case 39: // right
19208                 if (!this.keyboardNavigation) {
19209                     break;
19210                 }
19211                 dir = e.keyCode == 37 ? -1 : 1;
19212                 
19213                 if (e.ctrlKey){
19214                     newDate = this.moveYear(this.date, dir);
19215                     newViewDate = this.moveYear(this.viewDate, dir);
19216                 } else if (e.shiftKey){
19217                     newDate = this.moveMonth(this.date, dir);
19218                     newViewDate = this.moveMonth(this.viewDate, dir);
19219                 } else {
19220                     newDate = new Date(this.date);
19221                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19222                     newViewDate = new Date(this.viewDate);
19223                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19224                 }
19225                 if (this.dateWithinRange(newDate)){
19226                     this.date = newDate;
19227                     this.viewDate = newViewDate;
19228                     this.setValue(this.formatDate(this.date));
19229 //                    this.update();
19230                     e.preventDefault();
19231                     dateChanged = true;
19232                 }
19233                 break;
19234             case 38: // up
19235             case 40: // down
19236                 if (!this.keyboardNavigation) {
19237                     break;
19238                 }
19239                 dir = e.keyCode == 38 ? -1 : 1;
19240                 if (e.ctrlKey){
19241                     newDate = this.moveYear(this.date, dir);
19242                     newViewDate = this.moveYear(this.viewDate, dir);
19243                 } else if (e.shiftKey){
19244                     newDate = this.moveMonth(this.date, dir);
19245                     newViewDate = this.moveMonth(this.viewDate, dir);
19246                 } else {
19247                     newDate = new Date(this.date);
19248                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19249                     newViewDate = new Date(this.viewDate);
19250                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19251                 }
19252                 if (this.dateWithinRange(newDate)){
19253                     this.date = newDate;
19254                     this.viewDate = newViewDate;
19255                     this.setValue(this.formatDate(this.date));
19256 //                    this.update();
19257                     e.preventDefault();
19258                     dateChanged = true;
19259                 }
19260                 break;
19261             case 13: // enter
19262                 this.setValue(this.formatDate(this.date));
19263                 this.hidePopup();
19264                 e.preventDefault();
19265                 break;
19266             case 9: // tab
19267                 this.setValue(this.formatDate(this.date));
19268                 this.hidePopup();
19269                 break;
19270             case 16: // shift
19271             case 17: // ctrl
19272             case 18: // alt
19273                 break;
19274             default :
19275                 this.hidePopup();
19276                 
19277         }
19278     },
19279     
19280     
19281     onClick: function(e) 
19282     {
19283         e.stopPropagation();
19284         e.preventDefault();
19285         
19286         var target = e.getTarget();
19287         
19288         if(target.nodeName.toLowerCase() === 'i'){
19289             target = Roo.get(target).dom.parentNode;
19290         }
19291         
19292         var nodeName = target.nodeName;
19293         var className = target.className;
19294         var html = target.innerHTML;
19295         //Roo.log(nodeName);
19296         
19297         switch(nodeName.toLowerCase()) {
19298             case 'th':
19299                 switch(className) {
19300                     case 'switch':
19301                         this.showMode(1);
19302                         break;
19303                     case 'prev':
19304                     case 'next':
19305                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19306                         switch(this.viewMode){
19307                                 case 0:
19308                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19309                                         break;
19310                                 case 1:
19311                                 case 2:
19312                                         this.viewDate = this.moveYear(this.viewDate, dir);
19313                                         break;
19314                         }
19315                         this.fill();
19316                         break;
19317                     case 'today':
19318                         var date = new Date();
19319                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19320 //                        this.fill()
19321                         this.setValue(this.formatDate(this.date));
19322                         
19323                         this.hidePopup();
19324                         break;
19325                 }
19326                 break;
19327             case 'span':
19328                 if (className.indexOf('disabled') < 0) {
19329                     this.viewDate.setUTCDate(1);
19330                     if (className.indexOf('month') > -1) {
19331                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19332                     } else {
19333                         var year = parseInt(html, 10) || 0;
19334                         this.viewDate.setUTCFullYear(year);
19335                         
19336                     }
19337                     
19338                     if(this.singleMode){
19339                         this.setValue(this.formatDate(this.viewDate));
19340                         this.hidePopup();
19341                         return;
19342                     }
19343                     
19344                     this.showMode(-1);
19345                     this.fill();
19346                 }
19347                 break;
19348                 
19349             case 'td':
19350                 //Roo.log(className);
19351                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19352                     var day = parseInt(html, 10) || 1;
19353                     var year = this.viewDate.getUTCFullYear(),
19354                         month = this.viewDate.getUTCMonth();
19355
19356                     if (className.indexOf('old') > -1) {
19357                         if(month === 0 ){
19358                             month = 11;
19359                             year -= 1;
19360                         }else{
19361                             month -= 1;
19362                         }
19363                     } else if (className.indexOf('new') > -1) {
19364                         if (month == 11) {
19365                             month = 0;
19366                             year += 1;
19367                         } else {
19368                             month += 1;
19369                         }
19370                     }
19371                     //Roo.log([year,month,day]);
19372                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19373                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19374 //                    this.fill();
19375                     //Roo.log(this.formatDate(this.date));
19376                     this.setValue(this.formatDate(this.date));
19377                     this.hidePopup();
19378                 }
19379                 break;
19380         }
19381     },
19382     
19383     setStartDate: function(startDate)
19384     {
19385         this.startDate = startDate || -Infinity;
19386         if (this.startDate !== -Infinity) {
19387             this.startDate = this.parseDate(this.startDate);
19388         }
19389         this.update();
19390         this.updateNavArrows();
19391     },
19392
19393     setEndDate: function(endDate)
19394     {
19395         this.endDate = endDate || Infinity;
19396         if (this.endDate !== Infinity) {
19397             this.endDate = this.parseDate(this.endDate);
19398         }
19399         this.update();
19400         this.updateNavArrows();
19401     },
19402     
19403     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19404     {
19405         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19406         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19407             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19408         }
19409         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19410             return parseInt(d, 10);
19411         });
19412         this.update();
19413         this.updateNavArrows();
19414     },
19415     
19416     updateNavArrows: function() 
19417     {
19418         if(this.singleMode){
19419             return;
19420         }
19421         
19422         var d = new Date(this.viewDate),
19423         year = d.getUTCFullYear(),
19424         month = d.getUTCMonth();
19425         
19426         Roo.each(this.picker().select('.prev', true).elements, function(v){
19427             v.show();
19428             switch (this.viewMode) {
19429                 case 0:
19430
19431                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19432                         v.hide();
19433                     }
19434                     break;
19435                 case 1:
19436                 case 2:
19437                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19438                         v.hide();
19439                     }
19440                     break;
19441             }
19442         });
19443         
19444         Roo.each(this.picker().select('.next', true).elements, function(v){
19445             v.show();
19446             switch (this.viewMode) {
19447                 case 0:
19448
19449                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19450                         v.hide();
19451                     }
19452                     break;
19453                 case 1:
19454                 case 2:
19455                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19456                         v.hide();
19457                     }
19458                     break;
19459             }
19460         })
19461     },
19462     
19463     moveMonth: function(date, dir)
19464     {
19465         if (!dir) {
19466             return date;
19467         }
19468         var new_date = new Date(date.valueOf()),
19469         day = new_date.getUTCDate(),
19470         month = new_date.getUTCMonth(),
19471         mag = Math.abs(dir),
19472         new_month, test;
19473         dir = dir > 0 ? 1 : -1;
19474         if (mag == 1){
19475             test = dir == -1
19476             // If going back one month, make sure month is not current month
19477             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19478             ? function(){
19479                 return new_date.getUTCMonth() == month;
19480             }
19481             // If going forward one month, make sure month is as expected
19482             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19483             : function(){
19484                 return new_date.getUTCMonth() != new_month;
19485             };
19486             new_month = month + dir;
19487             new_date.setUTCMonth(new_month);
19488             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19489             if (new_month < 0 || new_month > 11) {
19490                 new_month = (new_month + 12) % 12;
19491             }
19492         } else {
19493             // For magnitudes >1, move one month at a time...
19494             for (var i=0; i<mag; i++) {
19495                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19496                 new_date = this.moveMonth(new_date, dir);
19497             }
19498             // ...then reset the day, keeping it in the new month
19499             new_month = new_date.getUTCMonth();
19500             new_date.setUTCDate(day);
19501             test = function(){
19502                 return new_month != new_date.getUTCMonth();
19503             };
19504         }
19505         // Common date-resetting loop -- if date is beyond end of month, make it
19506         // end of month
19507         while (test()){
19508             new_date.setUTCDate(--day);
19509             new_date.setUTCMonth(new_month);
19510         }
19511         return new_date;
19512     },
19513
19514     moveYear: function(date, dir)
19515     {
19516         return this.moveMonth(date, dir*12);
19517     },
19518
19519     dateWithinRange: function(date)
19520     {
19521         return date >= this.startDate && date <= this.endDate;
19522     },
19523
19524     
19525     remove: function() 
19526     {
19527         this.picker().remove();
19528     },
19529     
19530     validateValue : function(value)
19531     {
19532         if(this.getVisibilityEl().hasClass('hidden')){
19533             return true;
19534         }
19535         
19536         if(value.length < 1)  {
19537             if(this.allowBlank){
19538                 return true;
19539             }
19540             return false;
19541         }
19542         
19543         if(value.length < this.minLength){
19544             return false;
19545         }
19546         if(value.length > this.maxLength){
19547             return false;
19548         }
19549         if(this.vtype){
19550             var vt = Roo.form.VTypes;
19551             if(!vt[this.vtype](value, this)){
19552                 return false;
19553             }
19554         }
19555         if(typeof this.validator == "function"){
19556             var msg = this.validator(value);
19557             if(msg !== true){
19558                 return false;
19559             }
19560         }
19561         
19562         if(this.regex && !this.regex.test(value)){
19563             return false;
19564         }
19565         
19566         if(typeof(this.parseDate(value)) == 'undefined'){
19567             return false;
19568         }
19569         
19570         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19571             return false;
19572         }      
19573         
19574         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19575             return false;
19576         } 
19577         
19578         
19579         return true;
19580     },
19581     
19582     reset : function()
19583     {
19584         this.date = this.viewDate = '';
19585         
19586         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19587     }
19588    
19589 });
19590
19591 Roo.apply(Roo.bootstrap.DateField,  {
19592     
19593     head : {
19594         tag: 'thead',
19595         cn: [
19596         {
19597             tag: 'tr',
19598             cn: [
19599             {
19600                 tag: 'th',
19601                 cls: 'prev',
19602                 html: '<i class="fa fa-arrow-left"/>'
19603             },
19604             {
19605                 tag: 'th',
19606                 cls: 'switch',
19607                 colspan: '5'
19608             },
19609             {
19610                 tag: 'th',
19611                 cls: 'next',
19612                 html: '<i class="fa fa-arrow-right"/>'
19613             }
19614
19615             ]
19616         }
19617         ]
19618     },
19619     
19620     content : {
19621         tag: 'tbody',
19622         cn: [
19623         {
19624             tag: 'tr',
19625             cn: [
19626             {
19627                 tag: 'td',
19628                 colspan: '7'
19629             }
19630             ]
19631         }
19632         ]
19633     },
19634     
19635     footer : {
19636         tag: 'tfoot',
19637         cn: [
19638         {
19639             tag: 'tr',
19640             cn: [
19641             {
19642                 tag: 'th',
19643                 colspan: '7',
19644                 cls: 'today'
19645             }
19646                     
19647             ]
19648         }
19649         ]
19650     },
19651     
19652     dates:{
19653         en: {
19654             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19655             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19656             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19657             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19658             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19659             today: "Today"
19660         }
19661     },
19662     
19663     modes: [
19664     {
19665         clsName: 'days',
19666         navFnc: 'Month',
19667         navStep: 1
19668     },
19669     {
19670         clsName: 'months',
19671         navFnc: 'FullYear',
19672         navStep: 1
19673     },
19674     {
19675         clsName: 'years',
19676         navFnc: 'FullYear',
19677         navStep: 10
19678     }]
19679 });
19680
19681 Roo.apply(Roo.bootstrap.DateField,  {
19682   
19683     template : {
19684         tag: 'div',
19685         cls: 'datepicker dropdown-menu roo-dynamic',
19686         cn: [
19687         {
19688             tag: 'div',
19689             cls: 'datepicker-days',
19690             cn: [
19691             {
19692                 tag: 'table',
19693                 cls: 'table-condensed',
19694                 cn:[
19695                 Roo.bootstrap.DateField.head,
19696                 {
19697                     tag: 'tbody'
19698                 },
19699                 Roo.bootstrap.DateField.footer
19700                 ]
19701             }
19702             ]
19703         },
19704         {
19705             tag: 'div',
19706             cls: 'datepicker-months',
19707             cn: [
19708             {
19709                 tag: 'table',
19710                 cls: 'table-condensed',
19711                 cn:[
19712                 Roo.bootstrap.DateField.head,
19713                 Roo.bootstrap.DateField.content,
19714                 Roo.bootstrap.DateField.footer
19715                 ]
19716             }
19717             ]
19718         },
19719         {
19720             tag: 'div',
19721             cls: 'datepicker-years',
19722             cn: [
19723             {
19724                 tag: 'table',
19725                 cls: 'table-condensed',
19726                 cn:[
19727                 Roo.bootstrap.DateField.head,
19728                 Roo.bootstrap.DateField.content,
19729                 Roo.bootstrap.DateField.footer
19730                 ]
19731             }
19732             ]
19733         }
19734         ]
19735     }
19736 });
19737
19738  
19739
19740  /*
19741  * - LGPL
19742  *
19743  * TimeField
19744  * 
19745  */
19746
19747 /**
19748  * @class Roo.bootstrap.TimeField
19749  * @extends Roo.bootstrap.Input
19750  * Bootstrap DateField class
19751  * 
19752  * 
19753  * @constructor
19754  * Create a new TimeField
19755  * @param {Object} config The config object
19756  */
19757
19758 Roo.bootstrap.TimeField = function(config){
19759     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19760     this.addEvents({
19761             /**
19762              * @event show
19763              * Fires when this field show.
19764              * @param {Roo.bootstrap.DateField} thisthis
19765              * @param {Mixed} date The date value
19766              */
19767             show : true,
19768             /**
19769              * @event show
19770              * Fires when this field hide.
19771              * @param {Roo.bootstrap.DateField} this
19772              * @param {Mixed} date The date value
19773              */
19774             hide : true,
19775             /**
19776              * @event select
19777              * Fires when select a date.
19778              * @param {Roo.bootstrap.DateField} this
19779              * @param {Mixed} date The date value
19780              */
19781             select : true
19782         });
19783 };
19784
19785 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19786     
19787     /**
19788      * @cfg {String} format
19789      * The default time format string which can be overriden for localization support.  The format must be
19790      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19791      */
19792     format : "H:i",
19793        
19794     onRender: function(ct, position)
19795     {
19796         
19797         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19798                 
19799         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19800         
19801         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19802         
19803         this.pop = this.picker().select('>.datepicker-time',true).first();
19804         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19805         
19806         this.picker().on('mousedown', this.onMousedown, this);
19807         this.picker().on('click', this.onClick, this);
19808         
19809         this.picker().addClass('datepicker-dropdown');
19810     
19811         this.fillTime();
19812         this.update();
19813             
19814         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19815         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19816         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19817         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19818         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19819         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19820
19821     },
19822     
19823     fireKey: function(e){
19824         if (!this.picker().isVisible()){
19825             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19826                 this.show();
19827             }
19828             return;
19829         }
19830
19831         e.preventDefault();
19832         
19833         switch(e.keyCode){
19834             case 27: // escape
19835                 this.hide();
19836                 break;
19837             case 37: // left
19838             case 39: // right
19839                 this.onTogglePeriod();
19840                 break;
19841             case 38: // up
19842                 this.onIncrementMinutes();
19843                 break;
19844             case 40: // down
19845                 this.onDecrementMinutes();
19846                 break;
19847             case 13: // enter
19848             case 9: // tab
19849                 this.setTime();
19850                 break;
19851         }
19852     },
19853     
19854     onClick: function(e) {
19855         e.stopPropagation();
19856         e.preventDefault();
19857     },
19858     
19859     picker : function()
19860     {
19861         return this.el.select('.datepicker', true).first();
19862     },
19863     
19864     fillTime: function()
19865     {    
19866         var time = this.pop.select('tbody', true).first();
19867         
19868         time.dom.innerHTML = '';
19869         
19870         time.createChild({
19871             tag: 'tr',
19872             cn: [
19873                 {
19874                     tag: 'td',
19875                     cn: [
19876                         {
19877                             tag: 'a',
19878                             href: '#',
19879                             cls: 'btn',
19880                             cn: [
19881                                 {
19882                                     tag: 'span',
19883                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19884                                 }
19885                             ]
19886                         } 
19887                     ]
19888                 },
19889                 {
19890                     tag: 'td',
19891                     cls: 'separator'
19892                 },
19893                 {
19894                     tag: 'td',
19895                     cn: [
19896                         {
19897                             tag: 'a',
19898                             href: '#',
19899                             cls: 'btn',
19900                             cn: [
19901                                 {
19902                                     tag: 'span',
19903                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19904                                 }
19905                             ]
19906                         }
19907                     ]
19908                 },
19909                 {
19910                     tag: 'td',
19911                     cls: 'separator'
19912                 }
19913             ]
19914         });
19915         
19916         time.createChild({
19917             tag: 'tr',
19918             cn: [
19919                 {
19920                     tag: 'td',
19921                     cn: [
19922                         {
19923                             tag: 'span',
19924                             cls: 'timepicker-hour',
19925                             html: '00'
19926                         }  
19927                     ]
19928                 },
19929                 {
19930                     tag: 'td',
19931                     cls: 'separator',
19932                     html: ':'
19933                 },
19934                 {
19935                     tag: 'td',
19936                     cn: [
19937                         {
19938                             tag: 'span',
19939                             cls: 'timepicker-minute',
19940                             html: '00'
19941                         }  
19942                     ]
19943                 },
19944                 {
19945                     tag: 'td',
19946                     cls: 'separator'
19947                 },
19948                 {
19949                     tag: 'td',
19950                     cn: [
19951                         {
19952                             tag: 'button',
19953                             type: 'button',
19954                             cls: 'btn btn-primary period',
19955                             html: 'AM'
19956                             
19957                         }
19958                     ]
19959                 }
19960             ]
19961         });
19962         
19963         time.createChild({
19964             tag: 'tr',
19965             cn: [
19966                 {
19967                     tag: 'td',
19968                     cn: [
19969                         {
19970                             tag: 'a',
19971                             href: '#',
19972                             cls: 'btn',
19973                             cn: [
19974                                 {
19975                                     tag: 'span',
19976                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19977                                 }
19978                             ]
19979                         }
19980                     ]
19981                 },
19982                 {
19983                     tag: 'td',
19984                     cls: 'separator'
19985                 },
19986                 {
19987                     tag: 'td',
19988                     cn: [
19989                         {
19990                             tag: 'a',
19991                             href: '#',
19992                             cls: 'btn',
19993                             cn: [
19994                                 {
19995                                     tag: 'span',
19996                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19997                                 }
19998                             ]
19999                         }
20000                     ]
20001                 },
20002                 {
20003                     tag: 'td',
20004                     cls: 'separator'
20005                 }
20006             ]
20007         });
20008         
20009     },
20010     
20011     update: function()
20012     {
20013         
20014         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20015         
20016         this.fill();
20017     },
20018     
20019     fill: function() 
20020     {
20021         var hours = this.time.getHours();
20022         var minutes = this.time.getMinutes();
20023         var period = 'AM';
20024         
20025         if(hours > 11){
20026             period = 'PM';
20027         }
20028         
20029         if(hours == 0){
20030             hours = 12;
20031         }
20032         
20033         
20034         if(hours > 12){
20035             hours = hours - 12;
20036         }
20037         
20038         if(hours < 10){
20039             hours = '0' + hours;
20040         }
20041         
20042         if(minutes < 10){
20043             minutes = '0' + minutes;
20044         }
20045         
20046         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20047         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20048         this.pop.select('button', true).first().dom.innerHTML = period;
20049         
20050     },
20051     
20052     place: function()
20053     {   
20054         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20055         
20056         var cls = ['bottom'];
20057         
20058         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20059             cls.pop();
20060             cls.push('top');
20061         }
20062         
20063         cls.push('right');
20064         
20065         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20066             cls.pop();
20067             cls.push('left');
20068         }
20069         
20070         this.picker().addClass(cls.join('-'));
20071         
20072         var _this = this;
20073         
20074         Roo.each(cls, function(c){
20075             if(c == 'bottom'){
20076                 _this.picker().setTop(_this.inputEl().getHeight());
20077                 return;
20078             }
20079             if(c == 'top'){
20080                 _this.picker().setTop(0 - _this.picker().getHeight());
20081                 return;
20082             }
20083             
20084             if(c == 'left'){
20085                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20086                 return;
20087             }
20088             if(c == 'right'){
20089                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20090                 return;
20091             }
20092         });
20093         
20094     },
20095   
20096     onFocus : function()
20097     {
20098         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20099         this.show();
20100     },
20101     
20102     onBlur : function()
20103     {
20104         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20105         this.hide();
20106     },
20107     
20108     show : function()
20109     {
20110         this.picker().show();
20111         this.pop.show();
20112         this.update();
20113         this.place();
20114         
20115         this.fireEvent('show', this, this.date);
20116     },
20117     
20118     hide : function()
20119     {
20120         this.picker().hide();
20121         this.pop.hide();
20122         
20123         this.fireEvent('hide', this, this.date);
20124     },
20125     
20126     setTime : function()
20127     {
20128         this.hide();
20129         this.setValue(this.time.format(this.format));
20130         
20131         this.fireEvent('select', this, this.date);
20132         
20133         
20134     },
20135     
20136     onMousedown: function(e){
20137         e.stopPropagation();
20138         e.preventDefault();
20139     },
20140     
20141     onIncrementHours: function()
20142     {
20143         Roo.log('onIncrementHours');
20144         this.time = this.time.add(Date.HOUR, 1);
20145         this.update();
20146         
20147     },
20148     
20149     onDecrementHours: function()
20150     {
20151         Roo.log('onDecrementHours');
20152         this.time = this.time.add(Date.HOUR, -1);
20153         this.update();
20154     },
20155     
20156     onIncrementMinutes: function()
20157     {
20158         Roo.log('onIncrementMinutes');
20159         this.time = this.time.add(Date.MINUTE, 1);
20160         this.update();
20161     },
20162     
20163     onDecrementMinutes: function()
20164     {
20165         Roo.log('onDecrementMinutes');
20166         this.time = this.time.add(Date.MINUTE, -1);
20167         this.update();
20168     },
20169     
20170     onTogglePeriod: function()
20171     {
20172         Roo.log('onTogglePeriod');
20173         this.time = this.time.add(Date.HOUR, 12);
20174         this.update();
20175     }
20176     
20177    
20178 });
20179
20180 Roo.apply(Roo.bootstrap.TimeField,  {
20181     
20182     content : {
20183         tag: 'tbody',
20184         cn: [
20185             {
20186                 tag: 'tr',
20187                 cn: [
20188                 {
20189                     tag: 'td',
20190                     colspan: '7'
20191                 }
20192                 ]
20193             }
20194         ]
20195     },
20196     
20197     footer : {
20198         tag: 'tfoot',
20199         cn: [
20200             {
20201                 tag: 'tr',
20202                 cn: [
20203                 {
20204                     tag: 'th',
20205                     colspan: '7',
20206                     cls: '',
20207                     cn: [
20208                         {
20209                             tag: 'button',
20210                             cls: 'btn btn-info ok',
20211                             html: 'OK'
20212                         }
20213                     ]
20214                 }
20215
20216                 ]
20217             }
20218         ]
20219     }
20220 });
20221
20222 Roo.apply(Roo.bootstrap.TimeField,  {
20223   
20224     template : {
20225         tag: 'div',
20226         cls: 'datepicker dropdown-menu',
20227         cn: [
20228             {
20229                 tag: 'div',
20230                 cls: 'datepicker-time',
20231                 cn: [
20232                 {
20233                     tag: 'table',
20234                     cls: 'table-condensed',
20235                     cn:[
20236                     Roo.bootstrap.TimeField.content,
20237                     Roo.bootstrap.TimeField.footer
20238                     ]
20239                 }
20240                 ]
20241             }
20242         ]
20243     }
20244 });
20245
20246  
20247
20248  /*
20249  * - LGPL
20250  *
20251  * MonthField
20252  * 
20253  */
20254
20255 /**
20256  * @class Roo.bootstrap.MonthField
20257  * @extends Roo.bootstrap.Input
20258  * Bootstrap MonthField class
20259  * 
20260  * @cfg {String} language default en
20261  * 
20262  * @constructor
20263  * Create a new MonthField
20264  * @param {Object} config The config object
20265  */
20266
20267 Roo.bootstrap.MonthField = function(config){
20268     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20269     
20270     this.addEvents({
20271         /**
20272          * @event show
20273          * Fires when this field show.
20274          * @param {Roo.bootstrap.MonthField} this
20275          * @param {Mixed} date The date value
20276          */
20277         show : true,
20278         /**
20279          * @event show
20280          * Fires when this field hide.
20281          * @param {Roo.bootstrap.MonthField} this
20282          * @param {Mixed} date The date value
20283          */
20284         hide : true,
20285         /**
20286          * @event select
20287          * Fires when select a date.
20288          * @param {Roo.bootstrap.MonthField} this
20289          * @param {String} oldvalue The old value
20290          * @param {String} newvalue The new value
20291          */
20292         select : true
20293     });
20294 };
20295
20296 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20297     
20298     onRender: function(ct, position)
20299     {
20300         
20301         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20302         
20303         this.language = this.language || 'en';
20304         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20305         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20306         
20307         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20308         this.isInline = false;
20309         this.isInput = true;
20310         this.component = this.el.select('.add-on', true).first() || false;
20311         this.component = (this.component && this.component.length === 0) ? false : this.component;
20312         this.hasInput = this.component && this.inputEL().length;
20313         
20314         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20315         
20316         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20317         
20318         this.picker().on('mousedown', this.onMousedown, this);
20319         this.picker().on('click', this.onClick, this);
20320         
20321         this.picker().addClass('datepicker-dropdown');
20322         
20323         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20324             v.setStyle('width', '189px');
20325         });
20326         
20327         this.fillMonths();
20328         
20329         this.update();
20330         
20331         if(this.isInline) {
20332             this.show();
20333         }
20334         
20335     },
20336     
20337     setValue: function(v, suppressEvent)
20338     {   
20339         var o = this.getValue();
20340         
20341         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20342         
20343         this.update();
20344
20345         if(suppressEvent !== true){
20346             this.fireEvent('select', this, o, v);
20347         }
20348         
20349     },
20350     
20351     getValue: function()
20352     {
20353         return this.value;
20354     },
20355     
20356     onClick: function(e) 
20357     {
20358         e.stopPropagation();
20359         e.preventDefault();
20360         
20361         var target = e.getTarget();
20362         
20363         if(target.nodeName.toLowerCase() === 'i'){
20364             target = Roo.get(target).dom.parentNode;
20365         }
20366         
20367         var nodeName = target.nodeName;
20368         var className = target.className;
20369         var html = target.innerHTML;
20370         
20371         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20372             return;
20373         }
20374         
20375         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20376         
20377         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20378         
20379         this.hide();
20380                         
20381     },
20382     
20383     picker : function()
20384     {
20385         return this.pickerEl;
20386     },
20387     
20388     fillMonths: function()
20389     {    
20390         var i = 0;
20391         var months = this.picker().select('>.datepicker-months td', true).first();
20392         
20393         months.dom.innerHTML = '';
20394         
20395         while (i < 12) {
20396             var month = {
20397                 tag: 'span',
20398                 cls: 'month',
20399                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20400             };
20401             
20402             months.createChild(month);
20403         }
20404         
20405     },
20406     
20407     update: function()
20408     {
20409         var _this = this;
20410         
20411         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20412             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20413         }
20414         
20415         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20416             e.removeClass('active');
20417             
20418             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20419                 e.addClass('active');
20420             }
20421         })
20422     },
20423     
20424     place: function()
20425     {
20426         if(this.isInline) {
20427             return;
20428         }
20429         
20430         this.picker().removeClass(['bottom', 'top']);
20431         
20432         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20433             /*
20434              * place to the top of element!
20435              *
20436              */
20437             
20438             this.picker().addClass('top');
20439             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20440             
20441             return;
20442         }
20443         
20444         this.picker().addClass('bottom');
20445         
20446         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20447     },
20448     
20449     onFocus : function()
20450     {
20451         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20452         this.show();
20453     },
20454     
20455     onBlur : function()
20456     {
20457         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20458         
20459         var d = this.inputEl().getValue();
20460         
20461         this.setValue(d);
20462                 
20463         this.hide();
20464     },
20465     
20466     show : function()
20467     {
20468         this.picker().show();
20469         this.picker().select('>.datepicker-months', true).first().show();
20470         this.update();
20471         this.place();
20472         
20473         this.fireEvent('show', this, this.date);
20474     },
20475     
20476     hide : function()
20477     {
20478         if(this.isInline) {
20479             return;
20480         }
20481         this.picker().hide();
20482         this.fireEvent('hide', this, this.date);
20483         
20484     },
20485     
20486     onMousedown: function(e)
20487     {
20488         e.stopPropagation();
20489         e.preventDefault();
20490     },
20491     
20492     keyup: function(e)
20493     {
20494         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20495         this.update();
20496     },
20497
20498     fireKey: function(e)
20499     {
20500         if (!this.picker().isVisible()){
20501             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20502                 this.show();
20503             }
20504             return;
20505         }
20506         
20507         var dir;
20508         
20509         switch(e.keyCode){
20510             case 27: // escape
20511                 this.hide();
20512                 e.preventDefault();
20513                 break;
20514             case 37: // left
20515             case 39: // right
20516                 dir = e.keyCode == 37 ? -1 : 1;
20517                 
20518                 this.vIndex = this.vIndex + dir;
20519                 
20520                 if(this.vIndex < 0){
20521                     this.vIndex = 0;
20522                 }
20523                 
20524                 if(this.vIndex > 11){
20525                     this.vIndex = 11;
20526                 }
20527                 
20528                 if(isNaN(this.vIndex)){
20529                     this.vIndex = 0;
20530                 }
20531                 
20532                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20533                 
20534                 break;
20535             case 38: // up
20536             case 40: // down
20537                 
20538                 dir = e.keyCode == 38 ? -1 : 1;
20539                 
20540                 this.vIndex = this.vIndex + dir * 4;
20541                 
20542                 if(this.vIndex < 0){
20543                     this.vIndex = 0;
20544                 }
20545                 
20546                 if(this.vIndex > 11){
20547                     this.vIndex = 11;
20548                 }
20549                 
20550                 if(isNaN(this.vIndex)){
20551                     this.vIndex = 0;
20552                 }
20553                 
20554                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20555                 break;
20556                 
20557             case 13: // enter
20558                 
20559                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20560                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20561                 }
20562                 
20563                 this.hide();
20564                 e.preventDefault();
20565                 break;
20566             case 9: // tab
20567                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20568                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20569                 }
20570                 this.hide();
20571                 break;
20572             case 16: // shift
20573             case 17: // ctrl
20574             case 18: // alt
20575                 break;
20576             default :
20577                 this.hide();
20578                 
20579         }
20580     },
20581     
20582     remove: function() 
20583     {
20584         this.picker().remove();
20585     }
20586    
20587 });
20588
20589 Roo.apply(Roo.bootstrap.MonthField,  {
20590     
20591     content : {
20592         tag: 'tbody',
20593         cn: [
20594         {
20595             tag: 'tr',
20596             cn: [
20597             {
20598                 tag: 'td',
20599                 colspan: '7'
20600             }
20601             ]
20602         }
20603         ]
20604     },
20605     
20606     dates:{
20607         en: {
20608             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20609             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20610         }
20611     }
20612 });
20613
20614 Roo.apply(Roo.bootstrap.MonthField,  {
20615   
20616     template : {
20617         tag: 'div',
20618         cls: 'datepicker dropdown-menu roo-dynamic',
20619         cn: [
20620             {
20621                 tag: 'div',
20622                 cls: 'datepicker-months',
20623                 cn: [
20624                 {
20625                     tag: 'table',
20626                     cls: 'table-condensed',
20627                     cn:[
20628                         Roo.bootstrap.DateField.content
20629                     ]
20630                 }
20631                 ]
20632             }
20633         ]
20634     }
20635 });
20636
20637  
20638
20639  
20640  /*
20641  * - LGPL
20642  *
20643  * CheckBox
20644  * 
20645  */
20646
20647 /**
20648  * @class Roo.bootstrap.CheckBox
20649  * @extends Roo.bootstrap.Input
20650  * Bootstrap CheckBox class
20651  * 
20652  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20653  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20654  * @cfg {String} boxLabel The text that appears beside the checkbox
20655  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20656  * @cfg {Boolean} checked initnal the element
20657  * @cfg {Boolean} inline inline the element (default false)
20658  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20659  * @cfg {String} tooltip label tooltip
20660  * 
20661  * @constructor
20662  * Create a new CheckBox
20663  * @param {Object} config The config object
20664  */
20665
20666 Roo.bootstrap.CheckBox = function(config){
20667     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20668    
20669     this.addEvents({
20670         /**
20671         * @event check
20672         * Fires when the element is checked or unchecked.
20673         * @param {Roo.bootstrap.CheckBox} this This input
20674         * @param {Boolean} checked The new checked value
20675         */
20676        check : true,
20677        /**
20678         * @event click
20679         * Fires when the element is click.
20680         * @param {Roo.bootstrap.CheckBox} this This input
20681         */
20682        click : true
20683     });
20684     
20685 };
20686
20687 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20688   
20689     inputType: 'checkbox',
20690     inputValue: 1,
20691     valueOff: 0,
20692     boxLabel: false,
20693     checked: false,
20694     weight : false,
20695     inline: false,
20696     tooltip : '',
20697     
20698     getAutoCreate : function()
20699     {
20700         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20701         
20702         var id = Roo.id();
20703         
20704         var cfg = {};
20705         
20706         cfg.cls = 'form-group ' + this.inputType; //input-group
20707         
20708         if(this.inline){
20709             cfg.cls += ' ' + this.inputType + '-inline';
20710         }
20711         
20712         var input =  {
20713             tag: 'input',
20714             id : id,
20715             type : this.inputType,
20716             value : this.inputValue,
20717             cls : 'roo-' + this.inputType, //'form-box',
20718             placeholder : this.placeholder || ''
20719             
20720         };
20721         
20722         if(this.inputType != 'radio'){
20723             var hidden =  {
20724                 tag: 'input',
20725                 type : 'hidden',
20726                 cls : 'roo-hidden-value',
20727                 value : this.checked ? this.inputValue : this.valueOff
20728             };
20729         }
20730         
20731             
20732         if (this.weight) { // Validity check?
20733             cfg.cls += " " + this.inputType + "-" + this.weight;
20734         }
20735         
20736         if (this.disabled) {
20737             input.disabled=true;
20738         }
20739         
20740         if(this.checked){
20741             input.checked = this.checked;
20742         }
20743         
20744         if (this.name) {
20745             
20746             input.name = this.name;
20747             
20748             if(this.inputType != 'radio'){
20749                 hidden.name = this.name;
20750                 input.name = '_hidden_' + this.name;
20751             }
20752         }
20753         
20754         if (this.size) {
20755             input.cls += ' input-' + this.size;
20756         }
20757         
20758         var settings=this;
20759         
20760         ['xs','sm','md','lg'].map(function(size){
20761             if (settings[size]) {
20762                 cfg.cls += ' col-' + size + '-' + settings[size];
20763             }
20764         });
20765         
20766         var inputblock = input;
20767          
20768         if (this.before || this.after) {
20769             
20770             inputblock = {
20771                 cls : 'input-group',
20772                 cn :  [] 
20773             };
20774             
20775             if (this.before) {
20776                 inputblock.cn.push({
20777                     tag :'span',
20778                     cls : 'input-group-addon',
20779                     html : this.before
20780                 });
20781             }
20782             
20783             inputblock.cn.push(input);
20784             
20785             if(this.inputType != 'radio'){
20786                 inputblock.cn.push(hidden);
20787             }
20788             
20789             if (this.after) {
20790                 inputblock.cn.push({
20791                     tag :'span',
20792                     cls : 'input-group-addon',
20793                     html : this.after
20794                 });
20795             }
20796             
20797         }
20798         
20799         if (align ==='left' && this.fieldLabel.length) {
20800 //                Roo.log("left and has label");
20801             cfg.cn = [
20802                 {
20803                     tag: 'label',
20804                     'for' :  id,
20805                     cls : 'control-label',
20806                     html : this.fieldLabel
20807                 },
20808                 {
20809                     cls : "", 
20810                     cn: [
20811                         inputblock
20812                     ]
20813                 }
20814             ];
20815             
20816             if(this.labelWidth > 12){
20817                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20818             }
20819             
20820             if(this.labelWidth < 13 && this.labelmd == 0){
20821                 this.labelmd = this.labelWidth;
20822             }
20823             
20824             if(this.labellg > 0){
20825                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20826                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20827             }
20828             
20829             if(this.labelmd > 0){
20830                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20831                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20832             }
20833             
20834             if(this.labelsm > 0){
20835                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20836                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20837             }
20838             
20839             if(this.labelxs > 0){
20840                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20841                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20842             }
20843             
20844         } else if ( this.fieldLabel.length) {
20845 //                Roo.log(" label");
20846                 cfg.cn = [
20847                    
20848                     {
20849                         tag: this.boxLabel ? 'span' : 'label',
20850                         'for': id,
20851                         cls: 'control-label box-input-label',
20852                         //cls : 'input-group-addon',
20853                         html : this.fieldLabel
20854                     },
20855                     
20856                     inputblock
20857                     
20858                 ];
20859
20860         } else {
20861             
20862 //                Roo.log(" no label && no align");
20863                 cfg.cn = [  inputblock ] ;
20864                 
20865                 
20866         }
20867         
20868         if(this.boxLabel){
20869              var boxLabelCfg = {
20870                 tag: 'label',
20871                 //'for': id, // box label is handled by onclick - so no for...
20872                 cls: 'box-label',
20873                 html: this.boxLabel
20874             };
20875             
20876             if(this.tooltip){
20877                 boxLabelCfg.tooltip = this.tooltip;
20878             }
20879              
20880             cfg.cn.push(boxLabelCfg);
20881         }
20882         
20883         if(this.inputType != 'radio'){
20884             cfg.cn.push(hidden);
20885         }
20886         
20887         return cfg;
20888         
20889     },
20890     
20891     /**
20892      * return the real input element.
20893      */
20894     inputEl: function ()
20895     {
20896         return this.el.select('input.roo-' + this.inputType,true).first();
20897     },
20898     hiddenEl: function ()
20899     {
20900         return this.el.select('input.roo-hidden-value',true).first();
20901     },
20902     
20903     labelEl: function()
20904     {
20905         return this.el.select('label.control-label',true).first();
20906     },
20907     /* depricated... */
20908     
20909     label: function()
20910     {
20911         return this.labelEl();
20912     },
20913     
20914     boxLabelEl: function()
20915     {
20916         return this.el.select('label.box-label',true).first();
20917     },
20918     
20919     initEvents : function()
20920     {
20921 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20922         
20923         this.inputEl().on('click', this.onClick,  this);
20924         
20925         if (this.boxLabel) { 
20926             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20927         }
20928         
20929         this.startValue = this.getValue();
20930         
20931         if(this.groupId){
20932             Roo.bootstrap.CheckBox.register(this);
20933         }
20934     },
20935     
20936     onClick : function(e)
20937     {   
20938         if(this.fireEvent('click', this, e) !== false){
20939             this.setChecked(!this.checked);
20940         }
20941         
20942     },
20943     
20944     setChecked : function(state,suppressEvent)
20945     {
20946         this.startValue = this.getValue();
20947
20948         if(this.inputType == 'radio'){
20949             
20950             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20951                 e.dom.checked = false;
20952             });
20953             
20954             this.inputEl().dom.checked = true;
20955             
20956             this.inputEl().dom.value = this.inputValue;
20957             
20958             if(suppressEvent !== true){
20959                 this.fireEvent('check', this, true);
20960             }
20961             
20962             this.validate();
20963             
20964             return;
20965         }
20966         
20967         this.checked = state;
20968         
20969         this.inputEl().dom.checked = state;
20970         
20971         
20972         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20973         
20974         if(suppressEvent !== true){
20975             this.fireEvent('check', this, state);
20976         }
20977         
20978         this.validate();
20979     },
20980     
20981     getValue : function()
20982     {
20983         if(this.inputType == 'radio'){
20984             return this.getGroupValue();
20985         }
20986         
20987         return this.hiddenEl().dom.value;
20988         
20989     },
20990     
20991     getGroupValue : function()
20992     {
20993         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20994             return '';
20995         }
20996         
20997         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20998     },
20999     
21000     setValue : function(v,suppressEvent)
21001     {
21002         if(this.inputType == 'radio'){
21003             this.setGroupValue(v, suppressEvent);
21004             return;
21005         }
21006         
21007         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21008         
21009         this.validate();
21010     },
21011     
21012     setGroupValue : function(v, suppressEvent)
21013     {
21014         this.startValue = this.getValue();
21015         
21016         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21017             e.dom.checked = false;
21018             
21019             if(e.dom.value == v){
21020                 e.dom.checked = true;
21021             }
21022         });
21023         
21024         if(suppressEvent !== true){
21025             this.fireEvent('check', this, true);
21026         }
21027
21028         this.validate();
21029         
21030         return;
21031     },
21032     
21033     validate : function()
21034     {
21035         if(this.getVisibilityEl().hasClass('hidden')){
21036             return true;
21037         }
21038         
21039         if(
21040                 this.disabled || 
21041                 (this.inputType == 'radio' && this.validateRadio()) ||
21042                 (this.inputType == 'checkbox' && this.validateCheckbox())
21043         ){
21044             this.markValid();
21045             return true;
21046         }
21047         
21048         this.markInvalid();
21049         return false;
21050     },
21051     
21052     validateRadio : function()
21053     {
21054         if(this.getVisibilityEl().hasClass('hidden')){
21055             return true;
21056         }
21057         
21058         if(this.allowBlank){
21059             return true;
21060         }
21061         
21062         var valid = false;
21063         
21064         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21065             if(!e.dom.checked){
21066                 return;
21067             }
21068             
21069             valid = true;
21070             
21071             return false;
21072         });
21073         
21074         return valid;
21075     },
21076     
21077     validateCheckbox : function()
21078     {
21079         if(!this.groupId){
21080             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21081             //return (this.getValue() == this.inputValue) ? true : false;
21082         }
21083         
21084         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21085         
21086         if(!group){
21087             return false;
21088         }
21089         
21090         var r = false;
21091         
21092         for(var i in group){
21093             if(group[i].el.isVisible(true)){
21094                 r = false;
21095                 break;
21096             }
21097             
21098             r = true;
21099         }
21100         
21101         for(var i in group){
21102             if(r){
21103                 break;
21104             }
21105             
21106             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21107         }
21108         
21109         return r;
21110     },
21111     
21112     /**
21113      * Mark this field as valid
21114      */
21115     markValid : function()
21116     {
21117         var _this = this;
21118         
21119         this.fireEvent('valid', this);
21120         
21121         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21122         
21123         if(this.groupId){
21124             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21125         }
21126         
21127         if(label){
21128             label.markValid();
21129         }
21130
21131         if(this.inputType == 'radio'){
21132             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21133                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21134                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21135             });
21136             
21137             return;
21138         }
21139
21140         if(!this.groupId){
21141             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21142             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21143             return;
21144         }
21145         
21146         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21147         
21148         if(!group){
21149             return;
21150         }
21151         
21152         for(var i in group){
21153             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21154             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21155         }
21156     },
21157     
21158      /**
21159      * Mark this field as invalid
21160      * @param {String} msg The validation message
21161      */
21162     markInvalid : function(msg)
21163     {
21164         if(this.allowBlank){
21165             return;
21166         }
21167         
21168         var _this = this;
21169         
21170         this.fireEvent('invalid', this, msg);
21171         
21172         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21173         
21174         if(this.groupId){
21175             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21176         }
21177         
21178         if(label){
21179             label.markInvalid();
21180         }
21181             
21182         if(this.inputType == 'radio'){
21183             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21184                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21185                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21186             });
21187             
21188             return;
21189         }
21190         
21191         if(!this.groupId){
21192             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21193             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21194             return;
21195         }
21196         
21197         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21198         
21199         if(!group){
21200             return;
21201         }
21202         
21203         for(var i in group){
21204             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21205             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21206         }
21207         
21208     },
21209     
21210     clearInvalid : function()
21211     {
21212         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21213         
21214         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21215         
21216         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21217         
21218         if (label && label.iconEl) {
21219             label.iconEl.removeClass(label.validClass);
21220             label.iconEl.removeClass(label.invalidClass);
21221         }
21222     },
21223     
21224     disable : function()
21225     {
21226         if(this.inputType != 'radio'){
21227             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21228             return;
21229         }
21230         
21231         var _this = this;
21232         
21233         if(this.rendered){
21234             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21235                 _this.getActionEl().addClass(this.disabledClass);
21236                 e.dom.disabled = true;
21237             });
21238         }
21239         
21240         this.disabled = true;
21241         this.fireEvent("disable", this);
21242         return this;
21243     },
21244
21245     enable : function()
21246     {
21247         if(this.inputType != 'radio'){
21248             Roo.bootstrap.CheckBox.superclass.enable.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().removeClass(this.disabledClass);
21257                 e.dom.disabled = false;
21258             });
21259         }
21260         
21261         this.disabled = false;
21262         this.fireEvent("enable", this);
21263         return this;
21264     },
21265     
21266     setBoxLabel : function(v)
21267     {
21268         this.boxLabel = v;
21269         
21270         if(this.rendered){
21271             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21272         }
21273     }
21274
21275 });
21276
21277 Roo.apply(Roo.bootstrap.CheckBox, {
21278     
21279     groups: {},
21280     
21281      /**
21282     * register a CheckBox Group
21283     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21284     */
21285     register : function(checkbox)
21286     {
21287         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21288             this.groups[checkbox.groupId] = {};
21289         }
21290         
21291         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21292             return;
21293         }
21294         
21295         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21296         
21297     },
21298     /**
21299     * fetch a CheckBox Group based on the group ID
21300     * @param {string} the group ID
21301     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21302     */
21303     get: function(groupId) {
21304         if (typeof(this.groups[groupId]) == 'undefined') {
21305             return false;
21306         }
21307         
21308         return this.groups[groupId] ;
21309     }
21310     
21311     
21312 });
21313 /*
21314  * - LGPL
21315  *
21316  * RadioItem
21317  * 
21318  */
21319
21320 /**
21321  * @class Roo.bootstrap.Radio
21322  * @extends Roo.bootstrap.Component
21323  * Bootstrap Radio class
21324  * @cfg {String} boxLabel - the label associated
21325  * @cfg {String} value - the value of radio
21326  * 
21327  * @constructor
21328  * Create a new Radio
21329  * @param {Object} config The config object
21330  */
21331 Roo.bootstrap.Radio = function(config){
21332     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21333     
21334 };
21335
21336 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21337     
21338     boxLabel : '',
21339     
21340     value : '',
21341     
21342     getAutoCreate : function()
21343     {
21344         var cfg = {
21345             tag : 'div',
21346             cls : 'form-group radio',
21347             cn : [
21348                 {
21349                     tag : 'label',
21350                     cls : 'box-label',
21351                     html : this.boxLabel
21352                 }
21353             ]
21354         };
21355         
21356         return cfg;
21357     },
21358     
21359     initEvents : function() 
21360     {
21361         this.parent().register(this);
21362         
21363         this.el.on('click', this.onClick, this);
21364         
21365     },
21366     
21367     onClick : function(e)
21368     {
21369         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21370             this.setChecked(true);
21371         }
21372     },
21373     
21374     setChecked : function(state, suppressEvent)
21375     {
21376         this.parent().setValue(this.value, suppressEvent);
21377         
21378     },
21379     
21380     setBoxLabel : function(v)
21381     {
21382         this.boxLabel = v;
21383         
21384         if(this.rendered){
21385             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21386         }
21387     }
21388     
21389 });
21390  
21391
21392  /*
21393  * - LGPL
21394  *
21395  * Input
21396  * 
21397  */
21398
21399 /**
21400  * @class Roo.bootstrap.SecurePass
21401  * @extends Roo.bootstrap.Input
21402  * Bootstrap SecurePass class
21403  *
21404  * 
21405  * @constructor
21406  * Create a new SecurePass
21407  * @param {Object} config The config object
21408  */
21409  
21410 Roo.bootstrap.SecurePass = function (config) {
21411     // these go here, so the translation tool can replace them..
21412     this.errors = {
21413         PwdEmpty: "Please type a password, and then retype it to confirm.",
21414         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21415         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21416         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21417         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21418         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21419         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21420         TooWeak: "Your password is Too Weak."
21421     },
21422     this.meterLabel = "Password strength:";
21423     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21424     this.meterClass = [
21425         "roo-password-meter-tooweak", 
21426         "roo-password-meter-weak", 
21427         "roo-password-meter-medium", 
21428         "roo-password-meter-strong", 
21429         "roo-password-meter-grey"
21430     ];
21431     
21432     this.errors = {};
21433     
21434     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21435 }
21436
21437 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21438     /**
21439      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21440      * {
21441      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21442      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21443      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21444      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21445      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21446      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21447      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21448      * })
21449      */
21450     // private
21451     
21452     meterWidth: 300,
21453     errorMsg :'',    
21454     errors: false,
21455     imageRoot: '/',
21456     /**
21457      * @cfg {String/Object} Label for the strength meter (defaults to
21458      * 'Password strength:')
21459      */
21460     // private
21461     meterLabel: '',
21462     /**
21463      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21464      * ['Weak', 'Medium', 'Strong'])
21465      */
21466     // private    
21467     pwdStrengths: false,    
21468     // private
21469     strength: 0,
21470     // private
21471     _lastPwd: null,
21472     // private
21473     kCapitalLetter: 0,
21474     kSmallLetter: 1,
21475     kDigit: 2,
21476     kPunctuation: 3,
21477     
21478     insecure: false,
21479     // private
21480     initEvents: function ()
21481     {
21482         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21483
21484         if (this.el.is('input[type=password]') && Roo.isSafari) {
21485             this.el.on('keydown', this.SafariOnKeyDown, this);
21486         }
21487
21488         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21489     },
21490     // private
21491     onRender: function (ct, position)
21492     {
21493         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21494         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21495         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21496
21497         this.trigger.createChild({
21498                    cn: [
21499                     {
21500                     //id: 'PwdMeter',
21501                     tag: 'div',
21502                     cls: 'roo-password-meter-grey col-xs-12',
21503                     style: {
21504                         //width: 0,
21505                         //width: this.meterWidth + 'px'                                                
21506                         }
21507                     },
21508                     {                            
21509                          cls: 'roo-password-meter-text'                          
21510                     }
21511                 ]            
21512         });
21513
21514          
21515         if (this.hideTrigger) {
21516             this.trigger.setDisplayed(false);
21517         }
21518         this.setSize(this.width || '', this.height || '');
21519     },
21520     // private
21521     onDestroy: function ()
21522     {
21523         if (this.trigger) {
21524             this.trigger.removeAllListeners();
21525             this.trigger.remove();
21526         }
21527         if (this.wrap) {
21528             this.wrap.remove();
21529         }
21530         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21531     },
21532     // private
21533     checkStrength: function ()
21534     {
21535         var pwd = this.inputEl().getValue();
21536         if (pwd == this._lastPwd) {
21537             return;
21538         }
21539
21540         var strength;
21541         if (this.ClientSideStrongPassword(pwd)) {
21542             strength = 3;
21543         } else if (this.ClientSideMediumPassword(pwd)) {
21544             strength = 2;
21545         } else if (this.ClientSideWeakPassword(pwd)) {
21546             strength = 1;
21547         } else {
21548             strength = 0;
21549         }
21550         
21551         Roo.log('strength1: ' + strength);
21552         
21553         //var pm = this.trigger.child('div/div/div').dom;
21554         var pm = this.trigger.child('div/div');
21555         pm.removeClass(this.meterClass);
21556         pm.addClass(this.meterClass[strength]);
21557                 
21558         
21559         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21560                 
21561         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21562         
21563         this._lastPwd = pwd;
21564     },
21565     reset: function ()
21566     {
21567         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21568         
21569         this._lastPwd = '';
21570         
21571         var pm = this.trigger.child('div/div');
21572         pm.removeClass(this.meterClass);
21573         pm.addClass('roo-password-meter-grey');        
21574         
21575         
21576         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21577         
21578         pt.innerHTML = '';
21579         this.inputEl().dom.type='password';
21580     },
21581     // private
21582     validateValue: function (value)
21583     {
21584         
21585         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21586             return false;
21587         }
21588         if (value.length == 0) {
21589             if (this.allowBlank) {
21590                 this.clearInvalid();
21591                 return true;
21592             }
21593
21594             this.markInvalid(this.errors.PwdEmpty);
21595             this.errorMsg = this.errors.PwdEmpty;
21596             return false;
21597         }
21598         
21599         if(this.insecure){
21600             return true;
21601         }
21602         
21603         if ('[\x21-\x7e]*'.match(value)) {
21604             this.markInvalid(this.errors.PwdBadChar);
21605             this.errorMsg = this.errors.PwdBadChar;
21606             return false;
21607         }
21608         if (value.length < 6) {
21609             this.markInvalid(this.errors.PwdShort);
21610             this.errorMsg = this.errors.PwdShort;
21611             return false;
21612         }
21613         if (value.length > 16) {
21614             this.markInvalid(this.errors.PwdLong);
21615             this.errorMsg = this.errors.PwdLong;
21616             return false;
21617         }
21618         var strength;
21619         if (this.ClientSideStrongPassword(value)) {
21620             strength = 3;
21621         } else if (this.ClientSideMediumPassword(value)) {
21622             strength = 2;
21623         } else if (this.ClientSideWeakPassword(value)) {
21624             strength = 1;
21625         } else {
21626             strength = 0;
21627         }
21628
21629         
21630         if (strength < 2) {
21631             //this.markInvalid(this.errors.TooWeak);
21632             this.errorMsg = this.errors.TooWeak;
21633             //return false;
21634         }
21635         
21636         
21637         console.log('strength2: ' + strength);
21638         
21639         //var pm = this.trigger.child('div/div/div').dom;
21640         
21641         var pm = this.trigger.child('div/div');
21642         pm.removeClass(this.meterClass);
21643         pm.addClass(this.meterClass[strength]);
21644                 
21645         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21646                 
21647         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21648         
21649         this.errorMsg = ''; 
21650         return true;
21651     },
21652     // private
21653     CharacterSetChecks: function (type)
21654     {
21655         this.type = type;
21656         this.fResult = false;
21657     },
21658     // private
21659     isctype: function (character, type)
21660     {
21661         switch (type) {  
21662             case this.kCapitalLetter:
21663                 if (character >= 'A' && character <= 'Z') {
21664                     return true;
21665                 }
21666                 break;
21667             
21668             case this.kSmallLetter:
21669                 if (character >= 'a' && character <= 'z') {
21670                     return true;
21671                 }
21672                 break;
21673             
21674             case this.kDigit:
21675                 if (character >= '0' && character <= '9') {
21676                     return true;
21677                 }
21678                 break;
21679             
21680             case this.kPunctuation:
21681                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21682                     return true;
21683                 }
21684                 break;
21685             
21686             default:
21687                 return false;
21688         }
21689
21690     },
21691     // private
21692     IsLongEnough: function (pwd, size)
21693     {
21694         return !(pwd == null || isNaN(size) || pwd.length < size);
21695     },
21696     // private
21697     SpansEnoughCharacterSets: function (word, nb)
21698     {
21699         if (!this.IsLongEnough(word, nb))
21700         {
21701             return false;
21702         }
21703
21704         var characterSetChecks = new Array(
21705             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21706             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21707         );
21708         
21709         for (var index = 0; index < word.length; ++index) {
21710             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21711                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21712                     characterSetChecks[nCharSet].fResult = true;
21713                     break;
21714                 }
21715             }
21716         }
21717
21718         var nCharSets = 0;
21719         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21720             if (characterSetChecks[nCharSet].fResult) {
21721                 ++nCharSets;
21722             }
21723         }
21724
21725         if (nCharSets < nb) {
21726             return false;
21727         }
21728         return true;
21729     },
21730     // private
21731     ClientSideStrongPassword: function (pwd)
21732     {
21733         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21734     },
21735     // private
21736     ClientSideMediumPassword: function (pwd)
21737     {
21738         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21739     },
21740     // private
21741     ClientSideWeakPassword: function (pwd)
21742     {
21743         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21744     }
21745           
21746 })//<script type="text/javascript">
21747
21748 /*
21749  * Based  Ext JS Library 1.1.1
21750  * Copyright(c) 2006-2007, Ext JS, LLC.
21751  * LGPL
21752  *
21753  */
21754  
21755 /**
21756  * @class Roo.HtmlEditorCore
21757  * @extends Roo.Component
21758  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21759  *
21760  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21761  */
21762
21763 Roo.HtmlEditorCore = function(config){
21764     
21765     
21766     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21767     
21768     
21769     this.addEvents({
21770         /**
21771          * @event initialize
21772          * Fires when the editor is fully initialized (including the iframe)
21773          * @param {Roo.HtmlEditorCore} this
21774          */
21775         initialize: true,
21776         /**
21777          * @event activate
21778          * Fires when the editor is first receives the focus. Any insertion must wait
21779          * until after this event.
21780          * @param {Roo.HtmlEditorCore} this
21781          */
21782         activate: true,
21783          /**
21784          * @event beforesync
21785          * Fires before the textarea is updated with content from the editor iframe. Return false
21786          * to cancel the sync.
21787          * @param {Roo.HtmlEditorCore} this
21788          * @param {String} html
21789          */
21790         beforesync: true,
21791          /**
21792          * @event beforepush
21793          * Fires before the iframe editor is updated with content from the textarea. Return false
21794          * to cancel the push.
21795          * @param {Roo.HtmlEditorCore} this
21796          * @param {String} html
21797          */
21798         beforepush: true,
21799          /**
21800          * @event sync
21801          * Fires when the textarea is updated with content from the editor iframe.
21802          * @param {Roo.HtmlEditorCore} this
21803          * @param {String} html
21804          */
21805         sync: true,
21806          /**
21807          * @event push
21808          * Fires when the iframe editor is updated with content from the textarea.
21809          * @param {Roo.HtmlEditorCore} this
21810          * @param {String} html
21811          */
21812         push: true,
21813         
21814         /**
21815          * @event editorevent
21816          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21817          * @param {Roo.HtmlEditorCore} this
21818          */
21819         editorevent: true
21820         
21821     });
21822     
21823     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21824     
21825     // defaults : white / black...
21826     this.applyBlacklists();
21827     
21828     
21829     
21830 };
21831
21832
21833 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21834
21835
21836      /**
21837      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21838      */
21839     
21840     owner : false,
21841     
21842      /**
21843      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21844      *                        Roo.resizable.
21845      */
21846     resizable : false,
21847      /**
21848      * @cfg {Number} height (in pixels)
21849      */   
21850     height: 300,
21851    /**
21852      * @cfg {Number} width (in pixels)
21853      */   
21854     width: 500,
21855     
21856     /**
21857      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21858      * 
21859      */
21860     stylesheets: false,
21861     
21862     // id of frame..
21863     frameId: false,
21864     
21865     // private properties
21866     validationEvent : false,
21867     deferHeight: true,
21868     initialized : false,
21869     activated : false,
21870     sourceEditMode : false,
21871     onFocus : Roo.emptyFn,
21872     iframePad:3,
21873     hideMode:'offsets',
21874     
21875     clearUp: true,
21876     
21877     // blacklist + whitelisted elements..
21878     black: false,
21879     white: false,
21880      
21881     bodyCls : '',
21882
21883     /**
21884      * Protected method that will not generally be called directly. It
21885      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21886      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21887      */
21888     getDocMarkup : function(){
21889         // body styles..
21890         var st = '';
21891         
21892         // inherit styels from page...?? 
21893         if (this.stylesheets === false) {
21894             
21895             Roo.get(document.head).select('style').each(function(node) {
21896                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21897             });
21898             
21899             Roo.get(document.head).select('link').each(function(node) { 
21900                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21901             });
21902             
21903         } else if (!this.stylesheets.length) {
21904                 // simple..
21905                 st = '<style type="text/css">' +
21906                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21907                    '</style>';
21908         } else { 
21909             st = '<style type="text/css">' +
21910                     this.stylesheets +
21911                 '</style>';
21912         }
21913         
21914         st +=  '<style type="text/css">' +
21915             'IMG { cursor: pointer } ' +
21916         '</style>';
21917
21918         var cls = 'roo-htmleditor-body';
21919         
21920         if(this.bodyCls.length){
21921             cls += ' ' + this.bodyCls;
21922         }
21923         
21924         return '<html><head>' + st  +
21925             //<style type="text/css">' +
21926             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21927             //'</style>' +
21928             ' </head><body class="' +  cls + '"></body></html>';
21929     },
21930
21931     // private
21932     onRender : function(ct, position)
21933     {
21934         var _t = this;
21935         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21936         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21937         
21938         
21939         this.el.dom.style.border = '0 none';
21940         this.el.dom.setAttribute('tabIndex', -1);
21941         this.el.addClass('x-hidden hide');
21942         
21943         
21944         
21945         if(Roo.isIE){ // fix IE 1px bogus margin
21946             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21947         }
21948        
21949         
21950         this.frameId = Roo.id();
21951         
21952          
21953         
21954         var iframe = this.owner.wrap.createChild({
21955             tag: 'iframe',
21956             cls: 'form-control', // bootstrap..
21957             id: this.frameId,
21958             name: this.frameId,
21959             frameBorder : 'no',
21960             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21961         }, this.el
21962         );
21963         
21964         
21965         this.iframe = iframe.dom;
21966
21967          this.assignDocWin();
21968         
21969         this.doc.designMode = 'on';
21970        
21971         this.doc.open();
21972         this.doc.write(this.getDocMarkup());
21973         this.doc.close();
21974
21975         
21976         var task = { // must defer to wait for browser to be ready
21977             run : function(){
21978                 //console.log("run task?" + this.doc.readyState);
21979                 this.assignDocWin();
21980                 if(this.doc.body || this.doc.readyState == 'complete'){
21981                     try {
21982                         this.doc.designMode="on";
21983                     } catch (e) {
21984                         return;
21985                     }
21986                     Roo.TaskMgr.stop(task);
21987                     this.initEditor.defer(10, this);
21988                 }
21989             },
21990             interval : 10,
21991             duration: 10000,
21992             scope: this
21993         };
21994         Roo.TaskMgr.start(task);
21995
21996     },
21997
21998     // private
21999     onResize : function(w, h)
22000     {
22001          Roo.log('resize: ' +w + ',' + h );
22002         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22003         if(!this.iframe){
22004             return;
22005         }
22006         if(typeof w == 'number'){
22007             
22008             this.iframe.style.width = w + 'px';
22009         }
22010         if(typeof h == 'number'){
22011             
22012             this.iframe.style.height = h + 'px';
22013             if(this.doc){
22014                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22015             }
22016         }
22017         
22018     },
22019
22020     /**
22021      * Toggles the editor between standard and source edit mode.
22022      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22023      */
22024     toggleSourceEdit : function(sourceEditMode){
22025         
22026         this.sourceEditMode = sourceEditMode === true;
22027         
22028         if(this.sourceEditMode){
22029  
22030             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22031             
22032         }else{
22033             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22034             //this.iframe.className = '';
22035             this.deferFocus();
22036         }
22037         //this.setSize(this.owner.wrap.getSize());
22038         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22039     },
22040
22041     
22042   
22043
22044     /**
22045      * Protected method that will not generally be called directly. If you need/want
22046      * custom HTML cleanup, this is the method you should override.
22047      * @param {String} html The HTML to be cleaned
22048      * return {String} The cleaned HTML
22049      */
22050     cleanHtml : function(html){
22051         html = String(html);
22052         if(html.length > 5){
22053             if(Roo.isSafari){ // strip safari nonsense
22054                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22055             }
22056         }
22057         if(html == '&nbsp;'){
22058             html = '';
22059         }
22060         return html;
22061     },
22062
22063     /**
22064      * HTML Editor -> Textarea
22065      * Protected method that will not generally be called directly. Syncs the contents
22066      * of the editor iframe with the textarea.
22067      */
22068     syncValue : function(){
22069         if(this.initialized){
22070             var bd = (this.doc.body || this.doc.documentElement);
22071             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22072             var html = bd.innerHTML;
22073             if(Roo.isSafari){
22074                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22075                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22076                 if(m && m[1]){
22077                     html = '<div style="'+m[0]+'">' + html + '</div>';
22078                 }
22079             }
22080             html = this.cleanHtml(html);
22081             // fix up the special chars.. normaly like back quotes in word...
22082             // however we do not want to do this with chinese..
22083             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22084                 var cc = b.charCodeAt();
22085                 if (
22086                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22087                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22088                     (cc >= 0xf900 && cc < 0xfb00 )
22089                 ) {
22090                         return b;
22091                 }
22092                 return "&#"+cc+";" 
22093             });
22094             if(this.owner.fireEvent('beforesync', this, html) !== false){
22095                 this.el.dom.value = html;
22096                 this.owner.fireEvent('sync', this, html);
22097             }
22098         }
22099     },
22100
22101     /**
22102      * Protected method that will not generally be called directly. Pushes the value of the textarea
22103      * into the iframe editor.
22104      */
22105     pushValue : function(){
22106         if(this.initialized){
22107             var v = this.el.dom.value.trim();
22108             
22109 //            if(v.length < 1){
22110 //                v = '&#160;';
22111 //            }
22112             
22113             if(this.owner.fireEvent('beforepush', this, v) !== false){
22114                 var d = (this.doc.body || this.doc.documentElement);
22115                 d.innerHTML = v;
22116                 this.cleanUpPaste();
22117                 this.el.dom.value = d.innerHTML;
22118                 this.owner.fireEvent('push', this, v);
22119             }
22120         }
22121     },
22122
22123     // private
22124     deferFocus : function(){
22125         this.focus.defer(10, this);
22126     },
22127
22128     // doc'ed in Field
22129     focus : function(){
22130         if(this.win && !this.sourceEditMode){
22131             this.win.focus();
22132         }else{
22133             this.el.focus();
22134         }
22135     },
22136     
22137     assignDocWin: function()
22138     {
22139         var iframe = this.iframe;
22140         
22141          if(Roo.isIE){
22142             this.doc = iframe.contentWindow.document;
22143             this.win = iframe.contentWindow;
22144         } else {
22145 //            if (!Roo.get(this.frameId)) {
22146 //                return;
22147 //            }
22148 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22149 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22150             
22151             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22152                 return;
22153             }
22154             
22155             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22156             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22157         }
22158     },
22159     
22160     // private
22161     initEditor : function(){
22162         //console.log("INIT EDITOR");
22163         this.assignDocWin();
22164         
22165         
22166         
22167         this.doc.designMode="on";
22168         this.doc.open();
22169         this.doc.write(this.getDocMarkup());
22170         this.doc.close();
22171         
22172         var dbody = (this.doc.body || this.doc.documentElement);
22173         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22174         // this copies styles from the containing element into thsi one..
22175         // not sure why we need all of this..
22176         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22177         
22178         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22179         //ss['background-attachment'] = 'fixed'; // w3c
22180         dbody.bgProperties = 'fixed'; // ie
22181         //Roo.DomHelper.applyStyles(dbody, ss);
22182         Roo.EventManager.on(this.doc, {
22183             //'mousedown': this.onEditorEvent,
22184             'mouseup': this.onEditorEvent,
22185             'dblclick': this.onEditorEvent,
22186             'click': this.onEditorEvent,
22187             'keyup': this.onEditorEvent,
22188             buffer:100,
22189             scope: this
22190         });
22191         if(Roo.isGecko){
22192             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22193         }
22194         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22195             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22196         }
22197         this.initialized = true;
22198
22199         this.owner.fireEvent('initialize', this);
22200         this.pushValue();
22201     },
22202
22203     // private
22204     onDestroy : function(){
22205         
22206         
22207         
22208         if(this.rendered){
22209             
22210             //for (var i =0; i < this.toolbars.length;i++) {
22211             //    // fixme - ask toolbars for heights?
22212             //    this.toolbars[i].onDestroy();
22213            // }
22214             
22215             //this.wrap.dom.innerHTML = '';
22216             //this.wrap.remove();
22217         }
22218     },
22219
22220     // private
22221     onFirstFocus : function(){
22222         
22223         this.assignDocWin();
22224         
22225         
22226         this.activated = true;
22227          
22228     
22229         if(Roo.isGecko){ // prevent silly gecko errors
22230             this.win.focus();
22231             var s = this.win.getSelection();
22232             if(!s.focusNode || s.focusNode.nodeType != 3){
22233                 var r = s.getRangeAt(0);
22234                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22235                 r.collapse(true);
22236                 this.deferFocus();
22237             }
22238             try{
22239                 this.execCmd('useCSS', true);
22240                 this.execCmd('styleWithCSS', false);
22241             }catch(e){}
22242         }
22243         this.owner.fireEvent('activate', this);
22244     },
22245
22246     // private
22247     adjustFont: function(btn){
22248         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22249         //if(Roo.isSafari){ // safari
22250         //    adjust *= 2;
22251        // }
22252         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22253         if(Roo.isSafari){ // safari
22254             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22255             v =  (v < 10) ? 10 : v;
22256             v =  (v > 48) ? 48 : v;
22257             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22258             
22259         }
22260         
22261         
22262         v = Math.max(1, v+adjust);
22263         
22264         this.execCmd('FontSize', v  );
22265     },
22266
22267     onEditorEvent : function(e)
22268     {
22269         this.owner.fireEvent('editorevent', this, e);
22270       //  this.updateToolbar();
22271         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22272     },
22273
22274     insertTag : function(tg)
22275     {
22276         // could be a bit smarter... -> wrap the current selected tRoo..
22277         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22278             
22279             range = this.createRange(this.getSelection());
22280             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22281             wrappingNode.appendChild(range.extractContents());
22282             range.insertNode(wrappingNode);
22283
22284             return;
22285             
22286             
22287             
22288         }
22289         this.execCmd("formatblock",   tg);
22290         
22291     },
22292     
22293     insertText : function(txt)
22294     {
22295         
22296         
22297         var range = this.createRange();
22298         range.deleteContents();
22299                //alert(Sender.getAttribute('label'));
22300                
22301         range.insertNode(this.doc.createTextNode(txt));
22302     } ,
22303     
22304      
22305
22306     /**
22307      * Executes a Midas editor command on the editor document and performs necessary focus and
22308      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22309      * @param {String} cmd The Midas command
22310      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22311      */
22312     relayCmd : function(cmd, value){
22313         this.win.focus();
22314         this.execCmd(cmd, value);
22315         this.owner.fireEvent('editorevent', this);
22316         //this.updateToolbar();
22317         this.owner.deferFocus();
22318     },
22319
22320     /**
22321      * Executes a Midas editor command directly on the editor document.
22322      * For visual commands, you should use {@link #relayCmd} instead.
22323      * <b>This should only be called after the editor is initialized.</b>
22324      * @param {String} cmd The Midas command
22325      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22326      */
22327     execCmd : function(cmd, value){
22328         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22329         this.syncValue();
22330     },
22331  
22332  
22333    
22334     /**
22335      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22336      * to insert tRoo.
22337      * @param {String} text | dom node.. 
22338      */
22339     insertAtCursor : function(text)
22340     {
22341         
22342         if(!this.activated){
22343             return;
22344         }
22345         /*
22346         if(Roo.isIE){
22347             this.win.focus();
22348             var r = this.doc.selection.createRange();
22349             if(r){
22350                 r.collapse(true);
22351                 r.pasteHTML(text);
22352                 this.syncValue();
22353                 this.deferFocus();
22354             
22355             }
22356             return;
22357         }
22358         */
22359         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22360             this.win.focus();
22361             
22362             
22363             // from jquery ui (MIT licenced)
22364             var range, node;
22365             var win = this.win;
22366             
22367             if (win.getSelection && win.getSelection().getRangeAt) {
22368                 range = win.getSelection().getRangeAt(0);
22369                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22370                 range.insertNode(node);
22371             } else if (win.document.selection && win.document.selection.createRange) {
22372                 // no firefox support
22373                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22374                 win.document.selection.createRange().pasteHTML(txt);
22375             } else {
22376                 // no firefox support
22377                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22378                 this.execCmd('InsertHTML', txt);
22379             } 
22380             
22381             this.syncValue();
22382             
22383             this.deferFocus();
22384         }
22385     },
22386  // private
22387     mozKeyPress : function(e){
22388         if(e.ctrlKey){
22389             var c = e.getCharCode(), cmd;
22390           
22391             if(c > 0){
22392                 c = String.fromCharCode(c).toLowerCase();
22393                 switch(c){
22394                     case 'b':
22395                         cmd = 'bold';
22396                         break;
22397                     case 'i':
22398                         cmd = 'italic';
22399                         break;
22400                     
22401                     case 'u':
22402                         cmd = 'underline';
22403                         break;
22404                     
22405                     case 'v':
22406                         this.cleanUpPaste.defer(100, this);
22407                         return;
22408                         
22409                 }
22410                 if(cmd){
22411                     this.win.focus();
22412                     this.execCmd(cmd);
22413                     this.deferFocus();
22414                     e.preventDefault();
22415                 }
22416                 
22417             }
22418         }
22419     },
22420
22421     // private
22422     fixKeys : function(){ // load time branching for fastest keydown performance
22423         if(Roo.isIE){
22424             return function(e){
22425                 var k = e.getKey(), r;
22426                 if(k == e.TAB){
22427                     e.stopEvent();
22428                     r = this.doc.selection.createRange();
22429                     if(r){
22430                         r.collapse(true);
22431                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22432                         this.deferFocus();
22433                     }
22434                     return;
22435                 }
22436                 
22437                 if(k == e.ENTER){
22438                     r = this.doc.selection.createRange();
22439                     if(r){
22440                         var target = r.parentElement();
22441                         if(!target || target.tagName.toLowerCase() != 'li'){
22442                             e.stopEvent();
22443                             r.pasteHTML('<br />');
22444                             r.collapse(false);
22445                             r.select();
22446                         }
22447                     }
22448                 }
22449                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22450                     this.cleanUpPaste.defer(100, this);
22451                     return;
22452                 }
22453                 
22454                 
22455             };
22456         }else if(Roo.isOpera){
22457             return function(e){
22458                 var k = e.getKey();
22459                 if(k == e.TAB){
22460                     e.stopEvent();
22461                     this.win.focus();
22462                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22463                     this.deferFocus();
22464                 }
22465                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22466                     this.cleanUpPaste.defer(100, this);
22467                     return;
22468                 }
22469                 
22470             };
22471         }else if(Roo.isSafari){
22472             return function(e){
22473                 var k = e.getKey();
22474                 
22475                 if(k == e.TAB){
22476                     e.stopEvent();
22477                     this.execCmd('InsertText','\t');
22478                     this.deferFocus();
22479                     return;
22480                 }
22481                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22482                     this.cleanUpPaste.defer(100, this);
22483                     return;
22484                 }
22485                 
22486              };
22487         }
22488     }(),
22489     
22490     getAllAncestors: function()
22491     {
22492         var p = this.getSelectedNode();
22493         var a = [];
22494         if (!p) {
22495             a.push(p); // push blank onto stack..
22496             p = this.getParentElement();
22497         }
22498         
22499         
22500         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22501             a.push(p);
22502             p = p.parentNode;
22503         }
22504         a.push(this.doc.body);
22505         return a;
22506     },
22507     lastSel : false,
22508     lastSelNode : false,
22509     
22510     
22511     getSelection : function() 
22512     {
22513         this.assignDocWin();
22514         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22515     },
22516     
22517     getSelectedNode: function() 
22518     {
22519         // this may only work on Gecko!!!
22520         
22521         // should we cache this!!!!
22522         
22523         
22524         
22525          
22526         var range = this.createRange(this.getSelection()).cloneRange();
22527         
22528         if (Roo.isIE) {
22529             var parent = range.parentElement();
22530             while (true) {
22531                 var testRange = range.duplicate();
22532                 testRange.moveToElementText(parent);
22533                 if (testRange.inRange(range)) {
22534                     break;
22535                 }
22536                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22537                     break;
22538                 }
22539                 parent = parent.parentElement;
22540             }
22541             return parent;
22542         }
22543         
22544         // is ancestor a text element.
22545         var ac =  range.commonAncestorContainer;
22546         if (ac.nodeType == 3) {
22547             ac = ac.parentNode;
22548         }
22549         
22550         var ar = ac.childNodes;
22551          
22552         var nodes = [];
22553         var other_nodes = [];
22554         var has_other_nodes = false;
22555         for (var i=0;i<ar.length;i++) {
22556             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22557                 continue;
22558             }
22559             // fullly contained node.
22560             
22561             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22562                 nodes.push(ar[i]);
22563                 continue;
22564             }
22565             
22566             // probably selected..
22567             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22568                 other_nodes.push(ar[i]);
22569                 continue;
22570             }
22571             // outer..
22572             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22573                 continue;
22574             }
22575             
22576             
22577             has_other_nodes = true;
22578         }
22579         if (!nodes.length && other_nodes.length) {
22580             nodes= other_nodes;
22581         }
22582         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22583             return false;
22584         }
22585         
22586         return nodes[0];
22587     },
22588     createRange: function(sel)
22589     {
22590         // this has strange effects when using with 
22591         // top toolbar - not sure if it's a great idea.
22592         //this.editor.contentWindow.focus();
22593         if (typeof sel != "undefined") {
22594             try {
22595                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22596             } catch(e) {
22597                 return this.doc.createRange();
22598             }
22599         } else {
22600             return this.doc.createRange();
22601         }
22602     },
22603     getParentElement: function()
22604     {
22605         
22606         this.assignDocWin();
22607         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22608         
22609         var range = this.createRange(sel);
22610          
22611         try {
22612             var p = range.commonAncestorContainer;
22613             while (p.nodeType == 3) { // text node
22614                 p = p.parentNode;
22615             }
22616             return p;
22617         } catch (e) {
22618             return null;
22619         }
22620     
22621     },
22622     /***
22623      *
22624      * Range intersection.. the hard stuff...
22625      *  '-1' = before
22626      *  '0' = hits..
22627      *  '1' = after.
22628      *         [ -- selected range --- ]
22629      *   [fail]                        [fail]
22630      *
22631      *    basically..
22632      *      if end is before start or  hits it. fail.
22633      *      if start is after end or hits it fail.
22634      *
22635      *   if either hits (but other is outside. - then it's not 
22636      *   
22637      *    
22638      **/
22639     
22640     
22641     // @see http://www.thismuchiknow.co.uk/?p=64.
22642     rangeIntersectsNode : function(range, node)
22643     {
22644         var nodeRange = node.ownerDocument.createRange();
22645         try {
22646             nodeRange.selectNode(node);
22647         } catch (e) {
22648             nodeRange.selectNodeContents(node);
22649         }
22650     
22651         var rangeStartRange = range.cloneRange();
22652         rangeStartRange.collapse(true);
22653     
22654         var rangeEndRange = range.cloneRange();
22655         rangeEndRange.collapse(false);
22656     
22657         var nodeStartRange = nodeRange.cloneRange();
22658         nodeStartRange.collapse(true);
22659     
22660         var nodeEndRange = nodeRange.cloneRange();
22661         nodeEndRange.collapse(false);
22662     
22663         return rangeStartRange.compareBoundaryPoints(
22664                  Range.START_TO_START, nodeEndRange) == -1 &&
22665                rangeEndRange.compareBoundaryPoints(
22666                  Range.START_TO_START, nodeStartRange) == 1;
22667         
22668          
22669     },
22670     rangeCompareNode : function(range, node)
22671     {
22672         var nodeRange = node.ownerDocument.createRange();
22673         try {
22674             nodeRange.selectNode(node);
22675         } catch (e) {
22676             nodeRange.selectNodeContents(node);
22677         }
22678         
22679         
22680         range.collapse(true);
22681     
22682         nodeRange.collapse(true);
22683      
22684         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22685         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22686          
22687         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22688         
22689         var nodeIsBefore   =  ss == 1;
22690         var nodeIsAfter    = ee == -1;
22691         
22692         if (nodeIsBefore && nodeIsAfter) {
22693             return 0; // outer
22694         }
22695         if (!nodeIsBefore && nodeIsAfter) {
22696             return 1; //right trailed.
22697         }
22698         
22699         if (nodeIsBefore && !nodeIsAfter) {
22700             return 2;  // left trailed.
22701         }
22702         // fully contined.
22703         return 3;
22704     },
22705
22706     // private? - in a new class?
22707     cleanUpPaste :  function()
22708     {
22709         // cleans up the whole document..
22710         Roo.log('cleanuppaste');
22711         
22712         this.cleanUpChildren(this.doc.body);
22713         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22714         if (clean != this.doc.body.innerHTML) {
22715             this.doc.body.innerHTML = clean;
22716         }
22717         
22718     },
22719     
22720     cleanWordChars : function(input) {// change the chars to hex code
22721         var he = Roo.HtmlEditorCore;
22722         
22723         var output = input;
22724         Roo.each(he.swapCodes, function(sw) { 
22725             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22726             
22727             output = output.replace(swapper, sw[1]);
22728         });
22729         
22730         return output;
22731     },
22732     
22733     
22734     cleanUpChildren : function (n)
22735     {
22736         if (!n.childNodes.length) {
22737             return;
22738         }
22739         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22740            this.cleanUpChild(n.childNodes[i]);
22741         }
22742     },
22743     
22744     
22745         
22746     
22747     cleanUpChild : function (node)
22748     {
22749         var ed = this;
22750         //console.log(node);
22751         if (node.nodeName == "#text") {
22752             // clean up silly Windows -- stuff?
22753             return; 
22754         }
22755         if (node.nodeName == "#comment") {
22756             node.parentNode.removeChild(node);
22757             // clean up silly Windows -- stuff?
22758             return; 
22759         }
22760         var lcname = node.tagName.toLowerCase();
22761         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22762         // whitelist of tags..
22763         
22764         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22765             // remove node.
22766             node.parentNode.removeChild(node);
22767             return;
22768             
22769         }
22770         
22771         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22772         
22773         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22774         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22775         
22776         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22777         //    remove_keep_children = true;
22778         //}
22779         
22780         if (remove_keep_children) {
22781             this.cleanUpChildren(node);
22782             // inserts everything just before this node...
22783             while (node.childNodes.length) {
22784                 var cn = node.childNodes[0];
22785                 node.removeChild(cn);
22786                 node.parentNode.insertBefore(cn, node);
22787             }
22788             node.parentNode.removeChild(node);
22789             return;
22790         }
22791         
22792         if (!node.attributes || !node.attributes.length) {
22793             this.cleanUpChildren(node);
22794             return;
22795         }
22796         
22797         function cleanAttr(n,v)
22798         {
22799             
22800             if (v.match(/^\./) || v.match(/^\//)) {
22801                 return;
22802             }
22803             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22804                 return;
22805             }
22806             if (v.match(/^#/)) {
22807                 return;
22808             }
22809 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22810             node.removeAttribute(n);
22811             
22812         }
22813         
22814         var cwhite = this.cwhite;
22815         var cblack = this.cblack;
22816             
22817         function cleanStyle(n,v)
22818         {
22819             if (v.match(/expression/)) { //XSS?? should we even bother..
22820                 node.removeAttribute(n);
22821                 return;
22822             }
22823             
22824             var parts = v.split(/;/);
22825             var clean = [];
22826             
22827             Roo.each(parts, function(p) {
22828                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22829                 if (!p.length) {
22830                     return true;
22831                 }
22832                 var l = p.split(':').shift().replace(/\s+/g,'');
22833                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22834                 
22835                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22836 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22837                     //node.removeAttribute(n);
22838                     return true;
22839                 }
22840                 //Roo.log()
22841                 // only allow 'c whitelisted system attributes'
22842                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22843 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22844                     //node.removeAttribute(n);
22845                     return true;
22846                 }
22847                 
22848                 
22849                  
22850                 
22851                 clean.push(p);
22852                 return true;
22853             });
22854             if (clean.length) { 
22855                 node.setAttribute(n, clean.join(';'));
22856             } else {
22857                 node.removeAttribute(n);
22858             }
22859             
22860         }
22861         
22862         
22863         for (var i = node.attributes.length-1; i > -1 ; i--) {
22864             var a = node.attributes[i];
22865             //console.log(a);
22866             
22867             if (a.name.toLowerCase().substr(0,2)=='on')  {
22868                 node.removeAttribute(a.name);
22869                 continue;
22870             }
22871             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22872                 node.removeAttribute(a.name);
22873                 continue;
22874             }
22875             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22876                 cleanAttr(a.name,a.value); // fixme..
22877                 continue;
22878             }
22879             if (a.name == 'style') {
22880                 cleanStyle(a.name,a.value);
22881                 continue;
22882             }
22883             /// clean up MS crap..
22884             // tecnically this should be a list of valid class'es..
22885             
22886             
22887             if (a.name == 'class') {
22888                 if (a.value.match(/^Mso/)) {
22889                     node.className = '';
22890                 }
22891                 
22892                 if (a.value.match(/^body$/)) {
22893                     node.className = '';
22894                 }
22895                 continue;
22896             }
22897             
22898             // style cleanup!?
22899             // class cleanup?
22900             
22901         }
22902         
22903         
22904         this.cleanUpChildren(node);
22905         
22906         
22907     },
22908     
22909     /**
22910      * Clean up MS wordisms...
22911      */
22912     cleanWord : function(node)
22913     {
22914         
22915         
22916         if (!node) {
22917             this.cleanWord(this.doc.body);
22918             return;
22919         }
22920         if (node.nodeName == "#text") {
22921             // clean up silly Windows -- stuff?
22922             return; 
22923         }
22924         if (node.nodeName == "#comment") {
22925             node.parentNode.removeChild(node);
22926             // clean up silly Windows -- stuff?
22927             return; 
22928         }
22929         
22930         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22931             node.parentNode.removeChild(node);
22932             return;
22933         }
22934         
22935         // remove - but keep children..
22936         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22937             while (node.childNodes.length) {
22938                 var cn = node.childNodes[0];
22939                 node.removeChild(cn);
22940                 node.parentNode.insertBefore(cn, node);
22941             }
22942             node.parentNode.removeChild(node);
22943             this.iterateChildren(node, this.cleanWord);
22944             return;
22945         }
22946         // clean styles
22947         if (node.className.length) {
22948             
22949             var cn = node.className.split(/\W+/);
22950             var cna = [];
22951             Roo.each(cn, function(cls) {
22952                 if (cls.match(/Mso[a-zA-Z]+/)) {
22953                     return;
22954                 }
22955                 cna.push(cls);
22956             });
22957             node.className = cna.length ? cna.join(' ') : '';
22958             if (!cna.length) {
22959                 node.removeAttribute("class");
22960             }
22961         }
22962         
22963         if (node.hasAttribute("lang")) {
22964             node.removeAttribute("lang");
22965         }
22966         
22967         if (node.hasAttribute("style")) {
22968             
22969             var styles = node.getAttribute("style").split(";");
22970             var nstyle = [];
22971             Roo.each(styles, function(s) {
22972                 if (!s.match(/:/)) {
22973                     return;
22974                 }
22975                 var kv = s.split(":");
22976                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22977                     return;
22978                 }
22979                 // what ever is left... we allow.
22980                 nstyle.push(s);
22981             });
22982             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22983             if (!nstyle.length) {
22984                 node.removeAttribute('style');
22985             }
22986         }
22987         this.iterateChildren(node, this.cleanWord);
22988         
22989         
22990         
22991     },
22992     /**
22993      * iterateChildren of a Node, calling fn each time, using this as the scole..
22994      * @param {DomNode} node node to iterate children of.
22995      * @param {Function} fn method of this class to call on each item.
22996      */
22997     iterateChildren : function(node, fn)
22998     {
22999         if (!node.childNodes.length) {
23000                 return;
23001         }
23002         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23003            fn.call(this, node.childNodes[i])
23004         }
23005     },
23006     
23007     
23008     /**
23009      * cleanTableWidths.
23010      *
23011      * Quite often pasting from word etc.. results in tables with column and widths.
23012      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23013      *
23014      */
23015     cleanTableWidths : function(node)
23016     {
23017          
23018          
23019         if (!node) {
23020             this.cleanTableWidths(this.doc.body);
23021             return;
23022         }
23023         
23024         // ignore list...
23025         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23026             return; 
23027         }
23028         Roo.log(node.tagName);
23029         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23030             this.iterateChildren(node, this.cleanTableWidths);
23031             return;
23032         }
23033         if (node.hasAttribute('width')) {
23034             node.removeAttribute('width');
23035         }
23036         
23037          
23038         if (node.hasAttribute("style")) {
23039             // pretty basic...
23040             
23041             var styles = node.getAttribute("style").split(";");
23042             var nstyle = [];
23043             Roo.each(styles, function(s) {
23044                 if (!s.match(/:/)) {
23045                     return;
23046                 }
23047                 var kv = s.split(":");
23048                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23049                     return;
23050                 }
23051                 // what ever is left... we allow.
23052                 nstyle.push(s);
23053             });
23054             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23055             if (!nstyle.length) {
23056                 node.removeAttribute('style');
23057             }
23058         }
23059         
23060         this.iterateChildren(node, this.cleanTableWidths);
23061         
23062         
23063     },
23064     
23065     
23066     
23067     
23068     domToHTML : function(currentElement, depth, nopadtext) {
23069         
23070         depth = depth || 0;
23071         nopadtext = nopadtext || false;
23072     
23073         if (!currentElement) {
23074             return this.domToHTML(this.doc.body);
23075         }
23076         
23077         //Roo.log(currentElement);
23078         var j;
23079         var allText = false;
23080         var nodeName = currentElement.nodeName;
23081         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23082         
23083         if  (nodeName == '#text') {
23084             
23085             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23086         }
23087         
23088         
23089         var ret = '';
23090         if (nodeName != 'BODY') {
23091              
23092             var i = 0;
23093             // Prints the node tagName, such as <A>, <IMG>, etc
23094             if (tagName) {
23095                 var attr = [];
23096                 for(i = 0; i < currentElement.attributes.length;i++) {
23097                     // quoting?
23098                     var aname = currentElement.attributes.item(i).name;
23099                     if (!currentElement.attributes.item(i).value.length) {
23100                         continue;
23101                     }
23102                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23103                 }
23104                 
23105                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23106             } 
23107             else {
23108                 
23109                 // eack
23110             }
23111         } else {
23112             tagName = false;
23113         }
23114         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23115             return ret;
23116         }
23117         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23118             nopadtext = true;
23119         }
23120         
23121         
23122         // Traverse the tree
23123         i = 0;
23124         var currentElementChild = currentElement.childNodes.item(i);
23125         var allText = true;
23126         var innerHTML  = '';
23127         lastnode = '';
23128         while (currentElementChild) {
23129             // Formatting code (indent the tree so it looks nice on the screen)
23130             var nopad = nopadtext;
23131             if (lastnode == 'SPAN') {
23132                 nopad  = true;
23133             }
23134             // text
23135             if  (currentElementChild.nodeName == '#text') {
23136                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23137                 toadd = nopadtext ? toadd : toadd.trim();
23138                 if (!nopad && toadd.length > 80) {
23139                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23140                 }
23141                 innerHTML  += toadd;
23142                 
23143                 i++;
23144                 currentElementChild = currentElement.childNodes.item(i);
23145                 lastNode = '';
23146                 continue;
23147             }
23148             allText = false;
23149             
23150             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23151                 
23152             // Recursively traverse the tree structure of the child node
23153             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23154             lastnode = currentElementChild.nodeName;
23155             i++;
23156             currentElementChild=currentElement.childNodes.item(i);
23157         }
23158         
23159         ret += innerHTML;
23160         
23161         if (!allText) {
23162                 // The remaining code is mostly for formatting the tree
23163             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23164         }
23165         
23166         
23167         if (tagName) {
23168             ret+= "</"+tagName+">";
23169         }
23170         return ret;
23171         
23172     },
23173         
23174     applyBlacklists : function()
23175     {
23176         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23177         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23178         
23179         this.white = [];
23180         this.black = [];
23181         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23182             if (b.indexOf(tag) > -1) {
23183                 return;
23184             }
23185             this.white.push(tag);
23186             
23187         }, this);
23188         
23189         Roo.each(w, function(tag) {
23190             if (b.indexOf(tag) > -1) {
23191                 return;
23192             }
23193             if (this.white.indexOf(tag) > -1) {
23194                 return;
23195             }
23196             this.white.push(tag);
23197             
23198         }, this);
23199         
23200         
23201         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23202             if (w.indexOf(tag) > -1) {
23203                 return;
23204             }
23205             this.black.push(tag);
23206             
23207         }, this);
23208         
23209         Roo.each(b, function(tag) {
23210             if (w.indexOf(tag) > -1) {
23211                 return;
23212             }
23213             if (this.black.indexOf(tag) > -1) {
23214                 return;
23215             }
23216             this.black.push(tag);
23217             
23218         }, this);
23219         
23220         
23221         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23222         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23223         
23224         this.cwhite = [];
23225         this.cblack = [];
23226         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23227             if (b.indexOf(tag) > -1) {
23228                 return;
23229             }
23230             this.cwhite.push(tag);
23231             
23232         }, this);
23233         
23234         Roo.each(w, function(tag) {
23235             if (b.indexOf(tag) > -1) {
23236                 return;
23237             }
23238             if (this.cwhite.indexOf(tag) > -1) {
23239                 return;
23240             }
23241             this.cwhite.push(tag);
23242             
23243         }, this);
23244         
23245         
23246         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23247             if (w.indexOf(tag) > -1) {
23248                 return;
23249             }
23250             this.cblack.push(tag);
23251             
23252         }, this);
23253         
23254         Roo.each(b, function(tag) {
23255             if (w.indexOf(tag) > -1) {
23256                 return;
23257             }
23258             if (this.cblack.indexOf(tag) > -1) {
23259                 return;
23260             }
23261             this.cblack.push(tag);
23262             
23263         }, this);
23264     },
23265     
23266     setStylesheets : function(stylesheets)
23267     {
23268         if(typeof(stylesheets) == 'string'){
23269             Roo.get(this.iframe.contentDocument.head).createChild({
23270                 tag : 'link',
23271                 rel : 'stylesheet',
23272                 type : 'text/css',
23273                 href : stylesheets
23274             });
23275             
23276             return;
23277         }
23278         var _this = this;
23279      
23280         Roo.each(stylesheets, function(s) {
23281             if(!s.length){
23282                 return;
23283             }
23284             
23285             Roo.get(_this.iframe.contentDocument.head).createChild({
23286                 tag : 'link',
23287                 rel : 'stylesheet',
23288                 type : 'text/css',
23289                 href : s
23290             });
23291         });
23292
23293         
23294     },
23295     
23296     removeStylesheets : function()
23297     {
23298         var _this = this;
23299         
23300         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23301             s.remove();
23302         });
23303     },
23304     
23305     setStyle : function(style)
23306     {
23307         Roo.get(this.iframe.contentDocument.head).createChild({
23308             tag : 'style',
23309             type : 'text/css',
23310             html : style
23311         });
23312
23313         return;
23314     }
23315     
23316     // hide stuff that is not compatible
23317     /**
23318      * @event blur
23319      * @hide
23320      */
23321     /**
23322      * @event change
23323      * @hide
23324      */
23325     /**
23326      * @event focus
23327      * @hide
23328      */
23329     /**
23330      * @event specialkey
23331      * @hide
23332      */
23333     /**
23334      * @cfg {String} fieldClass @hide
23335      */
23336     /**
23337      * @cfg {String} focusClass @hide
23338      */
23339     /**
23340      * @cfg {String} autoCreate @hide
23341      */
23342     /**
23343      * @cfg {String} inputType @hide
23344      */
23345     /**
23346      * @cfg {String} invalidClass @hide
23347      */
23348     /**
23349      * @cfg {String} invalidText @hide
23350      */
23351     /**
23352      * @cfg {String} msgFx @hide
23353      */
23354     /**
23355      * @cfg {String} validateOnBlur @hide
23356      */
23357 });
23358
23359 Roo.HtmlEditorCore.white = [
23360         'area', 'br', 'img', 'input', 'hr', 'wbr',
23361         
23362        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23363        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23364        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23365        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23366        'table',   'ul',         'xmp', 
23367        
23368        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23369       'thead',   'tr', 
23370      
23371       'dir', 'menu', 'ol', 'ul', 'dl',
23372        
23373       'embed',  'object'
23374 ];
23375
23376
23377 Roo.HtmlEditorCore.black = [
23378     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23379         'applet', // 
23380         'base',   'basefont', 'bgsound', 'blink',  'body', 
23381         'frame',  'frameset', 'head',    'html',   'ilayer', 
23382         'iframe', 'layer',  'link',     'meta',    'object',   
23383         'script', 'style' ,'title',  'xml' // clean later..
23384 ];
23385 Roo.HtmlEditorCore.clean = [
23386     'script', 'style', 'title', 'xml'
23387 ];
23388 Roo.HtmlEditorCore.remove = [
23389     'font'
23390 ];
23391 // attributes..
23392
23393 Roo.HtmlEditorCore.ablack = [
23394     'on'
23395 ];
23396     
23397 Roo.HtmlEditorCore.aclean = [ 
23398     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23399 ];
23400
23401 // protocols..
23402 Roo.HtmlEditorCore.pwhite= [
23403         'http',  'https',  'mailto'
23404 ];
23405
23406 // white listed style attributes.
23407 Roo.HtmlEditorCore.cwhite= [
23408       //  'text-align', /// default is to allow most things..
23409       
23410          
23411 //        'font-size'//??
23412 ];
23413
23414 // black listed style attributes.
23415 Roo.HtmlEditorCore.cblack= [
23416       //  'font-size' -- this can be set by the project 
23417 ];
23418
23419
23420 Roo.HtmlEditorCore.swapCodes   =[ 
23421     [    8211, "--" ], 
23422     [    8212, "--" ], 
23423     [    8216,  "'" ],  
23424     [    8217, "'" ],  
23425     [    8220, '"' ],  
23426     [    8221, '"' ],  
23427     [    8226, "*" ],  
23428     [    8230, "..." ]
23429 ]; 
23430
23431     /*
23432  * - LGPL
23433  *
23434  * HtmlEditor
23435  * 
23436  */
23437
23438 /**
23439  * @class Roo.bootstrap.HtmlEditor
23440  * @extends Roo.bootstrap.TextArea
23441  * Bootstrap HtmlEditor class
23442
23443  * @constructor
23444  * Create a new HtmlEditor
23445  * @param {Object} config The config object
23446  */
23447
23448 Roo.bootstrap.HtmlEditor = function(config){
23449     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23450     if (!this.toolbars) {
23451         this.toolbars = [];
23452     }
23453     
23454     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23455     this.addEvents({
23456             /**
23457              * @event initialize
23458              * Fires when the editor is fully initialized (including the iframe)
23459              * @param {HtmlEditor} this
23460              */
23461             initialize: true,
23462             /**
23463              * @event activate
23464              * Fires when the editor is first receives the focus. Any insertion must wait
23465              * until after this event.
23466              * @param {HtmlEditor} this
23467              */
23468             activate: true,
23469              /**
23470              * @event beforesync
23471              * Fires before the textarea is updated with content from the editor iframe. Return false
23472              * to cancel the sync.
23473              * @param {HtmlEditor} this
23474              * @param {String} html
23475              */
23476             beforesync: true,
23477              /**
23478              * @event beforepush
23479              * Fires before the iframe editor is updated with content from the textarea. Return false
23480              * to cancel the push.
23481              * @param {HtmlEditor} this
23482              * @param {String} html
23483              */
23484             beforepush: true,
23485              /**
23486              * @event sync
23487              * Fires when the textarea is updated with content from the editor iframe.
23488              * @param {HtmlEditor} this
23489              * @param {String} html
23490              */
23491             sync: true,
23492              /**
23493              * @event push
23494              * Fires when the iframe editor is updated with content from the textarea.
23495              * @param {HtmlEditor} this
23496              * @param {String} html
23497              */
23498             push: true,
23499              /**
23500              * @event editmodechange
23501              * Fires when the editor switches edit modes
23502              * @param {HtmlEditor} this
23503              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23504              */
23505             editmodechange: true,
23506             /**
23507              * @event editorevent
23508              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23509              * @param {HtmlEditor} this
23510              */
23511             editorevent: true,
23512             /**
23513              * @event firstfocus
23514              * Fires when on first focus - needed by toolbars..
23515              * @param {HtmlEditor} this
23516              */
23517             firstfocus: true,
23518             /**
23519              * @event autosave
23520              * Auto save the htmlEditor value as a file into Events
23521              * @param {HtmlEditor} this
23522              */
23523             autosave: true,
23524             /**
23525              * @event savedpreview
23526              * preview the saved version of htmlEditor
23527              * @param {HtmlEditor} this
23528              */
23529             savedpreview: true
23530         });
23531 };
23532
23533
23534 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23535     
23536     
23537       /**
23538      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23539      */
23540     toolbars : false,
23541     
23542      /**
23543     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23544     */
23545     btns : [],
23546    
23547      /**
23548      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23549      *                        Roo.resizable.
23550      */
23551     resizable : false,
23552      /**
23553      * @cfg {Number} height (in pixels)
23554      */   
23555     height: 300,
23556    /**
23557      * @cfg {Number} width (in pixels)
23558      */   
23559     width: false,
23560     
23561     /**
23562      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23563      * 
23564      */
23565     stylesheets: false,
23566     
23567     // id of frame..
23568     frameId: false,
23569     
23570     // private properties
23571     validationEvent : false,
23572     deferHeight: true,
23573     initialized : false,
23574     activated : false,
23575     
23576     onFocus : Roo.emptyFn,
23577     iframePad:3,
23578     hideMode:'offsets',
23579     
23580     tbContainer : false,
23581     
23582     bodyCls : '',
23583     
23584     toolbarContainer :function() {
23585         return this.wrap.select('.x-html-editor-tb',true).first();
23586     },
23587
23588     /**
23589      * Protected method that will not generally be called directly. It
23590      * is called when the editor creates its toolbar. Override this method if you need to
23591      * add custom toolbar buttons.
23592      * @param {HtmlEditor} editor
23593      */
23594     createToolbar : function(){
23595         Roo.log('renewing');
23596         Roo.log("create toolbars");
23597         
23598         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23599         this.toolbars[0].render(this.toolbarContainer());
23600         
23601         return;
23602         
23603 //        if (!editor.toolbars || !editor.toolbars.length) {
23604 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23605 //        }
23606 //        
23607 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23608 //            editor.toolbars[i] = Roo.factory(
23609 //                    typeof(editor.toolbars[i]) == 'string' ?
23610 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23611 //                Roo.bootstrap.HtmlEditor);
23612 //            editor.toolbars[i].init(editor);
23613 //        }
23614     },
23615
23616      
23617     // private
23618     onRender : function(ct, position)
23619     {
23620        // Roo.log("Call onRender: " + this.xtype);
23621         var _t = this;
23622         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23623       
23624         this.wrap = this.inputEl().wrap({
23625             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23626         });
23627         
23628         this.editorcore.onRender(ct, position);
23629          
23630         if (this.resizable) {
23631             this.resizeEl = new Roo.Resizable(this.wrap, {
23632                 pinned : true,
23633                 wrap: true,
23634                 dynamic : true,
23635                 minHeight : this.height,
23636                 height: this.height,
23637                 handles : this.resizable,
23638                 width: this.width,
23639                 listeners : {
23640                     resize : function(r, w, h) {
23641                         _t.onResize(w,h); // -something
23642                     }
23643                 }
23644             });
23645             
23646         }
23647         this.createToolbar(this);
23648        
23649         
23650         if(!this.width && this.resizable){
23651             this.setSize(this.wrap.getSize());
23652         }
23653         if (this.resizeEl) {
23654             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23655             // should trigger onReize..
23656         }
23657         
23658     },
23659
23660     // private
23661     onResize : function(w, h)
23662     {
23663         Roo.log('resize: ' +w + ',' + h );
23664         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23665         var ew = false;
23666         var eh = false;
23667         
23668         if(this.inputEl() ){
23669             if(typeof w == 'number'){
23670                 var aw = w - this.wrap.getFrameWidth('lr');
23671                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23672                 ew = aw;
23673             }
23674             if(typeof h == 'number'){
23675                  var tbh = -11;  // fixme it needs to tool bar size!
23676                 for (var i =0; i < this.toolbars.length;i++) {
23677                     // fixme - ask toolbars for heights?
23678                     tbh += this.toolbars[i].el.getHeight();
23679                     //if (this.toolbars[i].footer) {
23680                     //    tbh += this.toolbars[i].footer.el.getHeight();
23681                     //}
23682                 }
23683               
23684                 
23685                 
23686                 
23687                 
23688                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23689                 ah -= 5; // knock a few pixes off for look..
23690                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23691                 var eh = ah;
23692             }
23693         }
23694         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23695         this.editorcore.onResize(ew,eh);
23696         
23697     },
23698
23699     /**
23700      * Toggles the editor between standard and source edit mode.
23701      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23702      */
23703     toggleSourceEdit : function(sourceEditMode)
23704     {
23705         this.editorcore.toggleSourceEdit(sourceEditMode);
23706         
23707         if(this.editorcore.sourceEditMode){
23708             Roo.log('editor - showing textarea');
23709             
23710 //            Roo.log('in');
23711 //            Roo.log(this.syncValue());
23712             this.syncValue();
23713             this.inputEl().removeClass(['hide', 'x-hidden']);
23714             this.inputEl().dom.removeAttribute('tabIndex');
23715             this.inputEl().focus();
23716         }else{
23717             Roo.log('editor - hiding textarea');
23718 //            Roo.log('out')
23719 //            Roo.log(this.pushValue()); 
23720             this.pushValue();
23721             
23722             this.inputEl().addClass(['hide', 'x-hidden']);
23723             this.inputEl().dom.setAttribute('tabIndex', -1);
23724             //this.deferFocus();
23725         }
23726          
23727         if(this.resizable){
23728             this.setSize(this.wrap.getSize());
23729         }
23730         
23731         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23732     },
23733  
23734     // private (for BoxComponent)
23735     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23736
23737     // private (for BoxComponent)
23738     getResizeEl : function(){
23739         return this.wrap;
23740     },
23741
23742     // private (for BoxComponent)
23743     getPositionEl : function(){
23744         return this.wrap;
23745     },
23746
23747     // private
23748     initEvents : function(){
23749         this.originalValue = this.getValue();
23750     },
23751
23752 //    /**
23753 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23754 //     * @method
23755 //     */
23756 //    markInvalid : Roo.emptyFn,
23757 //    /**
23758 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23759 //     * @method
23760 //     */
23761 //    clearInvalid : Roo.emptyFn,
23762
23763     setValue : function(v){
23764         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23765         this.editorcore.pushValue();
23766     },
23767
23768      
23769     // private
23770     deferFocus : function(){
23771         this.focus.defer(10, this);
23772     },
23773
23774     // doc'ed in Field
23775     focus : function(){
23776         this.editorcore.focus();
23777         
23778     },
23779       
23780
23781     // private
23782     onDestroy : function(){
23783         
23784         
23785         
23786         if(this.rendered){
23787             
23788             for (var i =0; i < this.toolbars.length;i++) {
23789                 // fixme - ask toolbars for heights?
23790                 this.toolbars[i].onDestroy();
23791             }
23792             
23793             this.wrap.dom.innerHTML = '';
23794             this.wrap.remove();
23795         }
23796     },
23797
23798     // private
23799     onFirstFocus : function(){
23800         //Roo.log("onFirstFocus");
23801         this.editorcore.onFirstFocus();
23802          for (var i =0; i < this.toolbars.length;i++) {
23803             this.toolbars[i].onFirstFocus();
23804         }
23805         
23806     },
23807     
23808     // private
23809     syncValue : function()
23810     {   
23811         this.editorcore.syncValue();
23812     },
23813     
23814     pushValue : function()
23815     {   
23816         this.editorcore.pushValue();
23817     }
23818      
23819     
23820     // hide stuff that is not compatible
23821     /**
23822      * @event blur
23823      * @hide
23824      */
23825     /**
23826      * @event change
23827      * @hide
23828      */
23829     /**
23830      * @event focus
23831      * @hide
23832      */
23833     /**
23834      * @event specialkey
23835      * @hide
23836      */
23837     /**
23838      * @cfg {String} fieldClass @hide
23839      */
23840     /**
23841      * @cfg {String} focusClass @hide
23842      */
23843     /**
23844      * @cfg {String} autoCreate @hide
23845      */
23846     /**
23847      * @cfg {String} inputType @hide
23848      */
23849     /**
23850      * @cfg {String} invalidClass @hide
23851      */
23852     /**
23853      * @cfg {String} invalidText @hide
23854      */
23855     /**
23856      * @cfg {String} msgFx @hide
23857      */
23858     /**
23859      * @cfg {String} validateOnBlur @hide
23860      */
23861 });
23862  
23863     
23864    
23865    
23866    
23867       
23868 Roo.namespace('Roo.bootstrap.htmleditor');
23869 /**
23870  * @class Roo.bootstrap.HtmlEditorToolbar1
23871  * Basic Toolbar
23872  * 
23873  * Usage:
23874  *
23875  new Roo.bootstrap.HtmlEditor({
23876     ....
23877     toolbars : [
23878         new Roo.bootstrap.HtmlEditorToolbar1({
23879             disable : { fonts: 1 , format: 1, ..., ... , ...],
23880             btns : [ .... ]
23881         })
23882     }
23883      
23884  * 
23885  * @cfg {Object} disable List of elements to disable..
23886  * @cfg {Array} btns List of additional buttons.
23887  * 
23888  * 
23889  * NEEDS Extra CSS? 
23890  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23891  */
23892  
23893 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23894 {
23895     
23896     Roo.apply(this, config);
23897     
23898     // default disabled, based on 'good practice'..
23899     this.disable = this.disable || {};
23900     Roo.applyIf(this.disable, {
23901         fontSize : true,
23902         colors : true,
23903         specialElements : true
23904     });
23905     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23906     
23907     this.editor = config.editor;
23908     this.editorcore = config.editor.editorcore;
23909     
23910     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23911     
23912     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23913     // dont call parent... till later.
23914 }
23915 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23916      
23917     bar : true,
23918     
23919     editor : false,
23920     editorcore : false,
23921     
23922     
23923     formats : [
23924         "p" ,  
23925         "h1","h2","h3","h4","h5","h6", 
23926         "pre", "code", 
23927         "abbr", "acronym", "address", "cite", "samp", "var",
23928         'div','span'
23929     ],
23930     
23931     onRender : function(ct, position)
23932     {
23933        // Roo.log("Call onRender: " + this.xtype);
23934         
23935        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23936        Roo.log(this.el);
23937        this.el.dom.style.marginBottom = '0';
23938        var _this = this;
23939        var editorcore = this.editorcore;
23940        var editor= this.editor;
23941        
23942        var children = [];
23943        var btn = function(id,cmd , toggle, handler, html){
23944        
23945             var  event = toggle ? 'toggle' : 'click';
23946        
23947             var a = {
23948                 size : 'sm',
23949                 xtype: 'Button',
23950                 xns: Roo.bootstrap,
23951                 glyphicon : id,
23952                 cmd : id || cmd,
23953                 enableToggle:toggle !== false,
23954                 html : html || '',
23955                 pressed : toggle ? false : null,
23956                 listeners : {}
23957             };
23958             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23959                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23960             };
23961             children.push(a);
23962             return a;
23963        }
23964        
23965     //    var cb_box = function...
23966         
23967         var style = {
23968                 xtype: 'Button',
23969                 size : 'sm',
23970                 xns: Roo.bootstrap,
23971                 glyphicon : 'font',
23972                 //html : 'submit'
23973                 menu : {
23974                     xtype: 'Menu',
23975                     xns: Roo.bootstrap,
23976                     items:  []
23977                 }
23978         };
23979         Roo.each(this.formats, function(f) {
23980             style.menu.items.push({
23981                 xtype :'MenuItem',
23982                 xns: Roo.bootstrap,
23983                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23984                 tagname : f,
23985                 listeners : {
23986                     click : function()
23987                     {
23988                         editorcore.insertTag(this.tagname);
23989                         editor.focus();
23990                     }
23991                 }
23992                 
23993             });
23994         });
23995         children.push(style);   
23996         
23997         btn('bold',false,true);
23998         btn('italic',false,true);
23999         btn('align-left', 'justifyleft',true);
24000         btn('align-center', 'justifycenter',true);
24001         btn('align-right' , 'justifyright',true);
24002         btn('link', false, false, function(btn) {
24003             //Roo.log("create link?");
24004             var url = prompt(this.createLinkText, this.defaultLinkValue);
24005             if(url && url != 'http:/'+'/'){
24006                 this.editorcore.relayCmd('createlink', url);
24007             }
24008         }),
24009         btn('list','insertunorderedlist',true);
24010         btn('pencil', false,true, function(btn){
24011                 Roo.log(this);
24012                 this.toggleSourceEdit(btn.pressed);
24013         });
24014         
24015         if (this.editor.btns.length > 0) {
24016             for (var i = 0; i<this.editor.btns.length; i++) {
24017                 children.push(this.editor.btns[i]);
24018             }
24019         }
24020         
24021         /*
24022         var cog = {
24023                 xtype: 'Button',
24024                 size : 'sm',
24025                 xns: Roo.bootstrap,
24026                 glyphicon : 'cog',
24027                 //html : 'submit'
24028                 menu : {
24029                     xtype: 'Menu',
24030                     xns: Roo.bootstrap,
24031                     items:  []
24032                 }
24033         };
24034         
24035         cog.menu.items.push({
24036             xtype :'MenuItem',
24037             xns: Roo.bootstrap,
24038             html : Clean styles,
24039             tagname : f,
24040             listeners : {
24041                 click : function()
24042                 {
24043                     editorcore.insertTag(this.tagname);
24044                     editor.focus();
24045                 }
24046             }
24047             
24048         });
24049        */
24050         
24051          
24052        this.xtype = 'NavSimplebar';
24053         
24054         for(var i=0;i< children.length;i++) {
24055             
24056             this.buttons.add(this.addxtypeChild(children[i]));
24057             
24058         }
24059         
24060         editor.on('editorevent', this.updateToolbar, this);
24061     },
24062     onBtnClick : function(id)
24063     {
24064        this.editorcore.relayCmd(id);
24065        this.editorcore.focus();
24066     },
24067     
24068     /**
24069      * Protected method that will not generally be called directly. It triggers
24070      * a toolbar update by reading the markup state of the current selection in the editor.
24071      */
24072     updateToolbar: function(){
24073
24074         if(!this.editorcore.activated){
24075             this.editor.onFirstFocus(); // is this neeed?
24076             return;
24077         }
24078
24079         var btns = this.buttons; 
24080         var doc = this.editorcore.doc;
24081         btns.get('bold').setActive(doc.queryCommandState('bold'));
24082         btns.get('italic').setActive(doc.queryCommandState('italic'));
24083         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24084         
24085         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24086         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24087         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24088         
24089         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24090         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24091          /*
24092         
24093         var ans = this.editorcore.getAllAncestors();
24094         if (this.formatCombo) {
24095             
24096             
24097             var store = this.formatCombo.store;
24098             this.formatCombo.setValue("");
24099             for (var i =0; i < ans.length;i++) {
24100                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24101                     // select it..
24102                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24103                     break;
24104                 }
24105             }
24106         }
24107         
24108         
24109         
24110         // hides menus... - so this cant be on a menu...
24111         Roo.bootstrap.MenuMgr.hideAll();
24112         */
24113         Roo.bootstrap.MenuMgr.hideAll();
24114         //this.editorsyncValue();
24115     },
24116     onFirstFocus: function() {
24117         this.buttons.each(function(item){
24118            item.enable();
24119         });
24120     },
24121     toggleSourceEdit : function(sourceEditMode){
24122         
24123           
24124         if(sourceEditMode){
24125             Roo.log("disabling buttons");
24126            this.buttons.each( function(item){
24127                 if(item.cmd != 'pencil'){
24128                     item.disable();
24129                 }
24130             });
24131           
24132         }else{
24133             Roo.log("enabling buttons");
24134             if(this.editorcore.initialized){
24135                 this.buttons.each( function(item){
24136                     item.enable();
24137                 });
24138             }
24139             
24140         }
24141         Roo.log("calling toggole on editor");
24142         // tell the editor that it's been pressed..
24143         this.editor.toggleSourceEdit(sourceEditMode);
24144        
24145     }
24146 });
24147
24148
24149
24150
24151
24152 /**
24153  * @class Roo.bootstrap.Table.AbstractSelectionModel
24154  * @extends Roo.util.Observable
24155  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24156  * implemented by descendant classes.  This class should not be directly instantiated.
24157  * @constructor
24158  */
24159 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24160     this.locked = false;
24161     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24162 };
24163
24164
24165 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24166     /** @ignore Called by the grid automatically. Do not call directly. */
24167     init : function(grid){
24168         this.grid = grid;
24169         this.initEvents();
24170     },
24171
24172     /**
24173      * Locks the selections.
24174      */
24175     lock : function(){
24176         this.locked = true;
24177     },
24178
24179     /**
24180      * Unlocks the selections.
24181      */
24182     unlock : function(){
24183         this.locked = false;
24184     },
24185
24186     /**
24187      * Returns true if the selections are locked.
24188      * @return {Boolean}
24189      */
24190     isLocked : function(){
24191         return this.locked;
24192     }
24193 });
24194 /**
24195  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24196  * @class Roo.bootstrap.Table.RowSelectionModel
24197  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24198  * It supports multiple selections and keyboard selection/navigation. 
24199  * @constructor
24200  * @param {Object} config
24201  */
24202
24203 Roo.bootstrap.Table.RowSelectionModel = function(config){
24204     Roo.apply(this, config);
24205     this.selections = new Roo.util.MixedCollection(false, function(o){
24206         return o.id;
24207     });
24208
24209     this.last = false;
24210     this.lastActive = false;
24211
24212     this.addEvents({
24213         /**
24214              * @event selectionchange
24215              * Fires when the selection changes
24216              * @param {SelectionModel} this
24217              */
24218             "selectionchange" : true,
24219         /**
24220              * @event afterselectionchange
24221              * Fires after the selection changes (eg. by key press or clicking)
24222              * @param {SelectionModel} this
24223              */
24224             "afterselectionchange" : true,
24225         /**
24226              * @event beforerowselect
24227              * Fires when a row is selected being selected, return false to cancel.
24228              * @param {SelectionModel} this
24229              * @param {Number} rowIndex The selected index
24230              * @param {Boolean} keepExisting False if other selections will be cleared
24231              */
24232             "beforerowselect" : true,
24233         /**
24234              * @event rowselect
24235              * Fires when a row is selected.
24236              * @param {SelectionModel} this
24237              * @param {Number} rowIndex The selected index
24238              * @param {Roo.data.Record} r The record
24239              */
24240             "rowselect" : true,
24241         /**
24242              * @event rowdeselect
24243              * Fires when a row is deselected.
24244              * @param {SelectionModel} this
24245              * @param {Number} rowIndex The selected index
24246              */
24247         "rowdeselect" : true
24248     });
24249     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24250     this.locked = false;
24251  };
24252
24253 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24254     /**
24255      * @cfg {Boolean} singleSelect
24256      * True to allow selection of only one row at a time (defaults to false)
24257      */
24258     singleSelect : false,
24259
24260     // private
24261     initEvents : function()
24262     {
24263
24264         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24265         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24266         //}else{ // allow click to work like normal
24267          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24268         //}
24269         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24270         this.grid.on("rowclick", this.handleMouseDown, this);
24271         
24272         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24273             "up" : function(e){
24274                 if(!e.shiftKey){
24275                     this.selectPrevious(e.shiftKey);
24276                 }else if(this.last !== false && this.lastActive !== false){
24277                     var last = this.last;
24278                     this.selectRange(this.last,  this.lastActive-1);
24279                     this.grid.getView().focusRow(this.lastActive);
24280                     if(last !== false){
24281                         this.last = last;
24282                     }
24283                 }else{
24284                     this.selectFirstRow();
24285                 }
24286                 this.fireEvent("afterselectionchange", this);
24287             },
24288             "down" : function(e){
24289                 if(!e.shiftKey){
24290                     this.selectNext(e.shiftKey);
24291                 }else if(this.last !== false && this.lastActive !== false){
24292                     var last = this.last;
24293                     this.selectRange(this.last,  this.lastActive+1);
24294                     this.grid.getView().focusRow(this.lastActive);
24295                     if(last !== false){
24296                         this.last = last;
24297                     }
24298                 }else{
24299                     this.selectFirstRow();
24300                 }
24301                 this.fireEvent("afterselectionchange", this);
24302             },
24303             scope: this
24304         });
24305         this.grid.store.on('load', function(){
24306             this.selections.clear();
24307         },this);
24308         /*
24309         var view = this.grid.view;
24310         view.on("refresh", this.onRefresh, this);
24311         view.on("rowupdated", this.onRowUpdated, this);
24312         view.on("rowremoved", this.onRemove, this);
24313         */
24314     },
24315
24316     // private
24317     onRefresh : function()
24318     {
24319         var ds = this.grid.store, i, v = this.grid.view;
24320         var s = this.selections;
24321         s.each(function(r){
24322             if((i = ds.indexOfId(r.id)) != -1){
24323                 v.onRowSelect(i);
24324             }else{
24325                 s.remove(r);
24326             }
24327         });
24328     },
24329
24330     // private
24331     onRemove : function(v, index, r){
24332         this.selections.remove(r);
24333     },
24334
24335     // private
24336     onRowUpdated : function(v, index, r){
24337         if(this.isSelected(r)){
24338             v.onRowSelect(index);
24339         }
24340     },
24341
24342     /**
24343      * Select records.
24344      * @param {Array} records The records to select
24345      * @param {Boolean} keepExisting (optional) True to keep existing selections
24346      */
24347     selectRecords : function(records, keepExisting)
24348     {
24349         if(!keepExisting){
24350             this.clearSelections();
24351         }
24352             var ds = this.grid.store;
24353         for(var i = 0, len = records.length; i < len; i++){
24354             this.selectRow(ds.indexOf(records[i]), true);
24355         }
24356     },
24357
24358     /**
24359      * Gets the number of selected rows.
24360      * @return {Number}
24361      */
24362     getCount : function(){
24363         return this.selections.length;
24364     },
24365
24366     /**
24367      * Selects the first row in the grid.
24368      */
24369     selectFirstRow : function(){
24370         this.selectRow(0);
24371     },
24372
24373     /**
24374      * Select the last row.
24375      * @param {Boolean} keepExisting (optional) True to keep existing selections
24376      */
24377     selectLastRow : function(keepExisting){
24378         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24379         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24380     },
24381
24382     /**
24383      * Selects the row immediately following the last selected row.
24384      * @param {Boolean} keepExisting (optional) True to keep existing selections
24385      */
24386     selectNext : function(keepExisting)
24387     {
24388             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24389             this.selectRow(this.last+1, keepExisting);
24390             this.grid.getView().focusRow(this.last);
24391         }
24392     },
24393
24394     /**
24395      * Selects the row that precedes the last selected row.
24396      * @param {Boolean} keepExisting (optional) True to keep existing selections
24397      */
24398     selectPrevious : function(keepExisting){
24399         if(this.last){
24400             this.selectRow(this.last-1, keepExisting);
24401             this.grid.getView().focusRow(this.last);
24402         }
24403     },
24404
24405     /**
24406      * Returns the selected records
24407      * @return {Array} Array of selected records
24408      */
24409     getSelections : function(){
24410         return [].concat(this.selections.items);
24411     },
24412
24413     /**
24414      * Returns the first selected record.
24415      * @return {Record}
24416      */
24417     getSelected : function(){
24418         return this.selections.itemAt(0);
24419     },
24420
24421
24422     /**
24423      * Clears all selections.
24424      */
24425     clearSelections : function(fast)
24426     {
24427         if(this.locked) {
24428             return;
24429         }
24430         if(fast !== true){
24431                 var ds = this.grid.store;
24432             var s = this.selections;
24433             s.each(function(r){
24434                 this.deselectRow(ds.indexOfId(r.id));
24435             }, this);
24436             s.clear();
24437         }else{
24438             this.selections.clear();
24439         }
24440         this.last = false;
24441     },
24442
24443
24444     /**
24445      * Selects all rows.
24446      */
24447     selectAll : function(){
24448         if(this.locked) {
24449             return;
24450         }
24451         this.selections.clear();
24452         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24453             this.selectRow(i, true);
24454         }
24455     },
24456
24457     /**
24458      * Returns True if there is a selection.
24459      * @return {Boolean}
24460      */
24461     hasSelection : function(){
24462         return this.selections.length > 0;
24463     },
24464
24465     /**
24466      * Returns True if the specified row is selected.
24467      * @param {Number/Record} record The record or index of the record to check
24468      * @return {Boolean}
24469      */
24470     isSelected : function(index){
24471             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24472         return (r && this.selections.key(r.id) ? true : false);
24473     },
24474
24475     /**
24476      * Returns True if the specified record id is selected.
24477      * @param {String} id The id of record to check
24478      * @return {Boolean}
24479      */
24480     isIdSelected : function(id){
24481         return (this.selections.key(id) ? true : false);
24482     },
24483
24484
24485     // private
24486     handleMouseDBClick : function(e, t){
24487         
24488     },
24489     // private
24490     handleMouseDown : function(e, t)
24491     {
24492             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24493         if(this.isLocked() || rowIndex < 0 ){
24494             return;
24495         };
24496         if(e.shiftKey && this.last !== false){
24497             var last = this.last;
24498             this.selectRange(last, rowIndex, e.ctrlKey);
24499             this.last = last; // reset the last
24500             t.focus();
24501     
24502         }else{
24503             var isSelected = this.isSelected(rowIndex);
24504             //Roo.log("select row:" + rowIndex);
24505             if(isSelected){
24506                 this.deselectRow(rowIndex);
24507             } else {
24508                         this.selectRow(rowIndex, true);
24509             }
24510     
24511             /*
24512                 if(e.button !== 0 && isSelected){
24513                 alert('rowIndex 2: ' + rowIndex);
24514                     view.focusRow(rowIndex);
24515                 }else if(e.ctrlKey && isSelected){
24516                     this.deselectRow(rowIndex);
24517                 }else if(!isSelected){
24518                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24519                     view.focusRow(rowIndex);
24520                 }
24521             */
24522         }
24523         this.fireEvent("afterselectionchange", this);
24524     },
24525     // private
24526     handleDragableRowClick :  function(grid, rowIndex, e) 
24527     {
24528         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24529             this.selectRow(rowIndex, false);
24530             grid.view.focusRow(rowIndex);
24531              this.fireEvent("afterselectionchange", this);
24532         }
24533     },
24534     
24535     /**
24536      * Selects multiple rows.
24537      * @param {Array} rows Array of the indexes of the row to select
24538      * @param {Boolean} keepExisting (optional) True to keep existing selections
24539      */
24540     selectRows : function(rows, keepExisting){
24541         if(!keepExisting){
24542             this.clearSelections();
24543         }
24544         for(var i = 0, len = rows.length; i < len; i++){
24545             this.selectRow(rows[i], true);
24546         }
24547     },
24548
24549     /**
24550      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24551      * @param {Number} startRow The index of the first row in the range
24552      * @param {Number} endRow The index of the last row in the range
24553      * @param {Boolean} keepExisting (optional) True to retain existing selections
24554      */
24555     selectRange : function(startRow, endRow, keepExisting){
24556         if(this.locked) {
24557             return;
24558         }
24559         if(!keepExisting){
24560             this.clearSelections();
24561         }
24562         if(startRow <= endRow){
24563             for(var i = startRow; i <= endRow; i++){
24564                 this.selectRow(i, true);
24565             }
24566         }else{
24567             for(var i = startRow; i >= endRow; i--){
24568                 this.selectRow(i, true);
24569             }
24570         }
24571     },
24572
24573     /**
24574      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24575      * @param {Number} startRow The index of the first row in the range
24576      * @param {Number} endRow The index of the last row in the range
24577      */
24578     deselectRange : function(startRow, endRow, preventViewNotify){
24579         if(this.locked) {
24580             return;
24581         }
24582         for(var i = startRow; i <= endRow; i++){
24583             this.deselectRow(i, preventViewNotify);
24584         }
24585     },
24586
24587     /**
24588      * Selects a row.
24589      * @param {Number} row The index of the row to select
24590      * @param {Boolean} keepExisting (optional) True to keep existing selections
24591      */
24592     selectRow : function(index, keepExisting, preventViewNotify)
24593     {
24594             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24595             return;
24596         }
24597         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24598             if(!keepExisting || this.singleSelect){
24599                 this.clearSelections();
24600             }
24601             
24602             var r = this.grid.store.getAt(index);
24603             //console.log('selectRow - record id :' + r.id);
24604             
24605             this.selections.add(r);
24606             this.last = this.lastActive = index;
24607             if(!preventViewNotify){
24608                 var proxy = new Roo.Element(
24609                                 this.grid.getRowDom(index)
24610                 );
24611                 proxy.addClass('bg-info info');
24612             }
24613             this.fireEvent("rowselect", this, index, r);
24614             this.fireEvent("selectionchange", this);
24615         }
24616     },
24617
24618     /**
24619      * Deselects a row.
24620      * @param {Number} row The index of the row to deselect
24621      */
24622     deselectRow : function(index, preventViewNotify)
24623     {
24624         if(this.locked) {
24625             return;
24626         }
24627         if(this.last == index){
24628             this.last = false;
24629         }
24630         if(this.lastActive == index){
24631             this.lastActive = false;
24632         }
24633         
24634         var r = this.grid.store.getAt(index);
24635         if (!r) {
24636             return;
24637         }
24638         
24639         this.selections.remove(r);
24640         //.console.log('deselectRow - record id :' + r.id);
24641         if(!preventViewNotify){
24642         
24643             var proxy = new Roo.Element(
24644                 this.grid.getRowDom(index)
24645             );
24646             proxy.removeClass('bg-info info');
24647         }
24648         this.fireEvent("rowdeselect", this, index);
24649         this.fireEvent("selectionchange", this);
24650     },
24651
24652     // private
24653     restoreLast : function(){
24654         if(this._last){
24655             this.last = this._last;
24656         }
24657     },
24658
24659     // private
24660     acceptsNav : function(row, col, cm){
24661         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24662     },
24663
24664     // private
24665     onEditorKey : function(field, e){
24666         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24667         if(k == e.TAB){
24668             e.stopEvent();
24669             ed.completeEdit();
24670             if(e.shiftKey){
24671                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24672             }else{
24673                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24674             }
24675         }else if(k == e.ENTER && !e.ctrlKey){
24676             e.stopEvent();
24677             ed.completeEdit();
24678             if(e.shiftKey){
24679                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24680             }else{
24681                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24682             }
24683         }else if(k == e.ESC){
24684             ed.cancelEdit();
24685         }
24686         if(newCell){
24687             g.startEditing(newCell[0], newCell[1]);
24688         }
24689     }
24690 });
24691 /*
24692  * Based on:
24693  * Ext JS Library 1.1.1
24694  * Copyright(c) 2006-2007, Ext JS, LLC.
24695  *
24696  * Originally Released Under LGPL - original licence link has changed is not relivant.
24697  *
24698  * Fork - LGPL
24699  * <script type="text/javascript">
24700  */
24701  
24702 /**
24703  * @class Roo.bootstrap.PagingToolbar
24704  * @extends Roo.bootstrap.NavSimplebar
24705  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24706  * @constructor
24707  * Create a new PagingToolbar
24708  * @param {Object} config The config object
24709  * @param {Roo.data.Store} store
24710  */
24711 Roo.bootstrap.PagingToolbar = function(config)
24712 {
24713     // old args format still supported... - xtype is prefered..
24714         // created from xtype...
24715     
24716     this.ds = config.dataSource;
24717     
24718     if (config.store && !this.ds) {
24719         this.store= Roo.factory(config.store, Roo.data);
24720         this.ds = this.store;
24721         this.ds.xmodule = this.xmodule || false;
24722     }
24723     
24724     this.toolbarItems = [];
24725     if (config.items) {
24726         this.toolbarItems = config.items;
24727     }
24728     
24729     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24730     
24731     this.cursor = 0;
24732     
24733     if (this.ds) { 
24734         this.bind(this.ds);
24735     }
24736     
24737     if (Roo.bootstrap.version == 4) {
24738         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24739     } else {
24740         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24741     }
24742     
24743 };
24744
24745 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24746     /**
24747      * @cfg {Roo.data.Store} dataSource
24748      * The underlying data store providing the paged data
24749      */
24750     /**
24751      * @cfg {String/HTMLElement/Element} container
24752      * container The id or element that will contain the toolbar
24753      */
24754     /**
24755      * @cfg {Boolean} displayInfo
24756      * True to display the displayMsg (defaults to false)
24757      */
24758     /**
24759      * @cfg {Number} pageSize
24760      * The number of records to display per page (defaults to 20)
24761      */
24762     pageSize: 20,
24763     /**
24764      * @cfg {String} displayMsg
24765      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24766      */
24767     displayMsg : 'Displaying {0} - {1} of {2}',
24768     /**
24769      * @cfg {String} emptyMsg
24770      * The message to display when no records are found (defaults to "No data to display")
24771      */
24772     emptyMsg : 'No data to display',
24773     /**
24774      * Customizable piece of the default paging text (defaults to "Page")
24775      * @type String
24776      */
24777     beforePageText : "Page",
24778     /**
24779      * Customizable piece of the default paging text (defaults to "of %0")
24780      * @type String
24781      */
24782     afterPageText : "of {0}",
24783     /**
24784      * Customizable piece of the default paging text (defaults to "First Page")
24785      * @type String
24786      */
24787     firstText : "First Page",
24788     /**
24789      * Customizable piece of the default paging text (defaults to "Previous Page")
24790      * @type String
24791      */
24792     prevText : "Previous Page",
24793     /**
24794      * Customizable piece of the default paging text (defaults to "Next Page")
24795      * @type String
24796      */
24797     nextText : "Next Page",
24798     /**
24799      * Customizable piece of the default paging text (defaults to "Last Page")
24800      * @type String
24801      */
24802     lastText : "Last Page",
24803     /**
24804      * Customizable piece of the default paging text (defaults to "Refresh")
24805      * @type String
24806      */
24807     refreshText : "Refresh",
24808
24809     buttons : false,
24810     // private
24811     onRender : function(ct, position) 
24812     {
24813         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24814         this.navgroup.parentId = this.id;
24815         this.navgroup.onRender(this.el, null);
24816         // add the buttons to the navgroup
24817         
24818         if(this.displayInfo){
24819             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24820             this.displayEl = this.el.select('.x-paging-info', true).first();
24821 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24822 //            this.displayEl = navel.el.select('span',true).first();
24823         }
24824         
24825         var _this = this;
24826         
24827         if(this.buttons){
24828             Roo.each(_this.buttons, function(e){ // this might need to use render????
24829                Roo.factory(e).render(_this.el);
24830             });
24831         }
24832             
24833         Roo.each(_this.toolbarItems, function(e) {
24834             _this.navgroup.addItem(e);
24835         });
24836         
24837         
24838         this.first = this.navgroup.addItem({
24839             tooltip: this.firstText,
24840             cls: "prev btn-outline-secondary",
24841             html : ' <i class="fa fa-step-backward"></i>',
24842             disabled: true,
24843             preventDefault: true,
24844             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24845         });
24846         
24847         this.prev =  this.navgroup.addItem({
24848             tooltip: this.prevText,
24849             cls: "prev btn-outline-secondary",
24850             html : ' <i class="fa fa-backward"></i>',
24851             disabled: true,
24852             preventDefault: true,
24853             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24854         });
24855     //this.addSeparator();
24856         
24857         
24858         var field = this.navgroup.addItem( {
24859             tagtype : 'span',
24860             cls : 'x-paging-position  btn-outline-secondary',
24861              disabled: true,
24862             html : this.beforePageText  +
24863                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24864                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24865          } ); //?? escaped?
24866         
24867         this.field = field.el.select('input', true).first();
24868         this.field.on("keydown", this.onPagingKeydown, this);
24869         this.field.on("focus", function(){this.dom.select();});
24870     
24871     
24872         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24873         //this.field.setHeight(18);
24874         //this.addSeparator();
24875         this.next = this.navgroup.addItem({
24876             tooltip: this.nextText,
24877             cls: "next btn-outline-secondary",
24878             html : ' <i class="fa fa-forward"></i>',
24879             disabled: true,
24880             preventDefault: true,
24881             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24882         });
24883         this.last = this.navgroup.addItem({
24884             tooltip: this.lastText,
24885             html : ' <i class="fa fa-step-forward"></i>',
24886             cls: "next btn-outline-secondary",
24887             disabled: true,
24888             preventDefault: true,
24889             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24890         });
24891     //this.addSeparator();
24892         this.loading = this.navgroup.addItem({
24893             tooltip: this.refreshText,
24894             cls: "btn-outline-secondary",
24895             html : ' <i class="fa fa-refresh"></i>',
24896             preventDefault: true,
24897             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24898         });
24899         
24900     },
24901
24902     // private
24903     updateInfo : function(){
24904         if(this.displayEl){
24905             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24906             var msg = count == 0 ?
24907                 this.emptyMsg :
24908                 String.format(
24909                     this.displayMsg,
24910                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24911                 );
24912             this.displayEl.update(msg);
24913         }
24914     },
24915
24916     // private
24917     onLoad : function(ds, r, o)
24918     {
24919         this.cursor = o.params.start ? o.params.start : 0;
24920         
24921         var d = this.getPageData(),
24922             ap = d.activePage,
24923             ps = d.pages;
24924         
24925         
24926         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24927         this.field.dom.value = ap;
24928         this.first.setDisabled(ap == 1);
24929         this.prev.setDisabled(ap == 1);
24930         this.next.setDisabled(ap == ps);
24931         this.last.setDisabled(ap == ps);
24932         this.loading.enable();
24933         this.updateInfo();
24934     },
24935
24936     // private
24937     getPageData : function(){
24938         var total = this.ds.getTotalCount();
24939         return {
24940             total : total,
24941             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24942             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24943         };
24944     },
24945
24946     // private
24947     onLoadError : function(){
24948         this.loading.enable();
24949     },
24950
24951     // private
24952     onPagingKeydown : function(e){
24953         var k = e.getKey();
24954         var d = this.getPageData();
24955         if(k == e.RETURN){
24956             var v = this.field.dom.value, pageNum;
24957             if(!v || isNaN(pageNum = parseInt(v, 10))){
24958                 this.field.dom.value = d.activePage;
24959                 return;
24960             }
24961             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24962             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24963             e.stopEvent();
24964         }
24965         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))
24966         {
24967           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24968           this.field.dom.value = pageNum;
24969           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24970           e.stopEvent();
24971         }
24972         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24973         {
24974           var v = this.field.dom.value, pageNum; 
24975           var increment = (e.shiftKey) ? 10 : 1;
24976           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24977                 increment *= -1;
24978           }
24979           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24980             this.field.dom.value = d.activePage;
24981             return;
24982           }
24983           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24984           {
24985             this.field.dom.value = parseInt(v, 10) + increment;
24986             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24987             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24988           }
24989           e.stopEvent();
24990         }
24991     },
24992
24993     // private
24994     beforeLoad : function(){
24995         if(this.loading){
24996             this.loading.disable();
24997         }
24998     },
24999
25000     // private
25001     onClick : function(which){
25002         
25003         var ds = this.ds;
25004         if (!ds) {
25005             return;
25006         }
25007         
25008         switch(which){
25009             case "first":
25010                 ds.load({params:{start: 0, limit: this.pageSize}});
25011             break;
25012             case "prev":
25013                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25014             break;
25015             case "next":
25016                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25017             break;
25018             case "last":
25019                 var total = ds.getTotalCount();
25020                 var extra = total % this.pageSize;
25021                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25022                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25023             break;
25024             case "refresh":
25025                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25026             break;
25027         }
25028     },
25029
25030     /**
25031      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25032      * @param {Roo.data.Store} store The data store to unbind
25033      */
25034     unbind : function(ds){
25035         ds.un("beforeload", this.beforeLoad, this);
25036         ds.un("load", this.onLoad, this);
25037         ds.un("loadexception", this.onLoadError, this);
25038         ds.un("remove", this.updateInfo, this);
25039         ds.un("add", this.updateInfo, this);
25040         this.ds = undefined;
25041     },
25042
25043     /**
25044      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25045      * @param {Roo.data.Store} store The data store to bind
25046      */
25047     bind : function(ds){
25048         ds.on("beforeload", this.beforeLoad, this);
25049         ds.on("load", this.onLoad, this);
25050         ds.on("loadexception", this.onLoadError, this);
25051         ds.on("remove", this.updateInfo, this);
25052         ds.on("add", this.updateInfo, this);
25053         this.ds = ds;
25054     }
25055 });/*
25056  * - LGPL
25057  *
25058  * element
25059  * 
25060  */
25061
25062 /**
25063  * @class Roo.bootstrap.MessageBar
25064  * @extends Roo.bootstrap.Component
25065  * Bootstrap MessageBar class
25066  * @cfg {String} html contents of the MessageBar
25067  * @cfg {String} weight (info | success | warning | danger) default info
25068  * @cfg {String} beforeClass insert the bar before the given class
25069  * @cfg {Boolean} closable (true | false) default false
25070  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25071  * 
25072  * @constructor
25073  * Create a new Element
25074  * @param {Object} config The config object
25075  */
25076
25077 Roo.bootstrap.MessageBar = function(config){
25078     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25079 };
25080
25081 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25082     
25083     html: '',
25084     weight: 'info',
25085     closable: false,
25086     fixed: false,
25087     beforeClass: 'bootstrap-sticky-wrap',
25088     
25089     getAutoCreate : function(){
25090         
25091         var cfg = {
25092             tag: 'div',
25093             cls: 'alert alert-dismissable alert-' + this.weight,
25094             cn: [
25095                 {
25096                     tag: 'span',
25097                     cls: 'message',
25098                     html: this.html || ''
25099                 }
25100             ]
25101         };
25102         
25103         if(this.fixed){
25104             cfg.cls += ' alert-messages-fixed';
25105         }
25106         
25107         if(this.closable){
25108             cfg.cn.push({
25109                 tag: 'button',
25110                 cls: 'close',
25111                 html: 'x'
25112             });
25113         }
25114         
25115         return cfg;
25116     },
25117     
25118     onRender : function(ct, position)
25119     {
25120         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25121         
25122         if(!this.el){
25123             var cfg = Roo.apply({},  this.getAutoCreate());
25124             cfg.id = Roo.id();
25125             
25126             if (this.cls) {
25127                 cfg.cls += ' ' + this.cls;
25128             }
25129             if (this.style) {
25130                 cfg.style = this.style;
25131             }
25132             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25133             
25134             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25135         }
25136         
25137         this.el.select('>button.close').on('click', this.hide, this);
25138         
25139     },
25140     
25141     show : function()
25142     {
25143         if (!this.rendered) {
25144             this.render();
25145         }
25146         
25147         this.el.show();
25148         
25149         this.fireEvent('show', this);
25150         
25151     },
25152     
25153     hide : function()
25154     {
25155         if (!this.rendered) {
25156             this.render();
25157         }
25158         
25159         this.el.hide();
25160         
25161         this.fireEvent('hide', this);
25162     },
25163     
25164     update : function()
25165     {
25166 //        var e = this.el.dom.firstChild;
25167 //        
25168 //        if(this.closable){
25169 //            e = e.nextSibling;
25170 //        }
25171 //        
25172 //        e.data = this.html || '';
25173
25174         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25175     }
25176    
25177 });
25178
25179  
25180
25181      /*
25182  * - LGPL
25183  *
25184  * Graph
25185  * 
25186  */
25187
25188
25189 /**
25190  * @class Roo.bootstrap.Graph
25191  * @extends Roo.bootstrap.Component
25192  * Bootstrap Graph class
25193 > Prameters
25194  -sm {number} sm 4
25195  -md {number} md 5
25196  @cfg {String} graphtype  bar | vbar | pie
25197  @cfg {number} g_x coodinator | centre x (pie)
25198  @cfg {number} g_y coodinator | centre y (pie)
25199  @cfg {number} g_r radius (pie)
25200  @cfg {number} g_height height of the chart (respected by all elements in the set)
25201  @cfg {number} g_width width of the chart (respected by all elements in the set)
25202  @cfg {Object} title The title of the chart
25203     
25204  -{Array}  values
25205  -opts (object) options for the chart 
25206      o {
25207      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25208      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25209      o vgutter (number)
25210      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.
25211      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25212      o to
25213      o stretch (boolean)
25214      o }
25215  -opts (object) options for the pie
25216      o{
25217      o cut
25218      o startAngle (number)
25219      o endAngle (number)
25220      } 
25221  *
25222  * @constructor
25223  * Create a new Input
25224  * @param {Object} config The config object
25225  */
25226
25227 Roo.bootstrap.Graph = function(config){
25228     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25229     
25230     this.addEvents({
25231         // img events
25232         /**
25233          * @event click
25234          * The img click event for the img.
25235          * @param {Roo.EventObject} e
25236          */
25237         "click" : true
25238     });
25239 };
25240
25241 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25242     
25243     sm: 4,
25244     md: 5,
25245     graphtype: 'bar',
25246     g_height: 250,
25247     g_width: 400,
25248     g_x: 50,
25249     g_y: 50,
25250     g_r: 30,
25251     opts:{
25252         //g_colors: this.colors,
25253         g_type: 'soft',
25254         g_gutter: '20%'
25255
25256     },
25257     title : false,
25258
25259     getAutoCreate : function(){
25260         
25261         var cfg = {
25262             tag: 'div',
25263             html : null
25264         };
25265         
25266         
25267         return  cfg;
25268     },
25269
25270     onRender : function(ct,position){
25271         
25272         
25273         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25274         
25275         if (typeof(Raphael) == 'undefined') {
25276             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25277             return;
25278         }
25279         
25280         this.raphael = Raphael(this.el.dom);
25281         
25282                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25283                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25284                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25285                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25286                 /*
25287                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25288                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25289                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25290                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25291                 
25292                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25293                 r.barchart(330, 10, 300, 220, data1);
25294                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25295                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25296                 */
25297                 
25298                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25299                 // r.barchart(30, 30, 560, 250,  xdata, {
25300                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25301                 //     axis : "0 0 1 1",
25302                 //     axisxlabels :  xdata
25303                 //     //yvalues : cols,
25304                    
25305                 // });
25306 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25307 //        
25308 //        this.load(null,xdata,{
25309 //                axis : "0 0 1 1",
25310 //                axisxlabels :  xdata
25311 //                });
25312
25313     },
25314
25315     load : function(graphtype,xdata,opts)
25316     {
25317         this.raphael.clear();
25318         if(!graphtype) {
25319             graphtype = this.graphtype;
25320         }
25321         if(!opts){
25322             opts = this.opts;
25323         }
25324         var r = this.raphael,
25325             fin = function () {
25326                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25327             },
25328             fout = function () {
25329                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25330             },
25331             pfin = function() {
25332                 this.sector.stop();
25333                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25334
25335                 if (this.label) {
25336                     this.label[0].stop();
25337                     this.label[0].attr({ r: 7.5 });
25338                     this.label[1].attr({ "font-weight": 800 });
25339                 }
25340             },
25341             pfout = function() {
25342                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25343
25344                 if (this.label) {
25345                     this.label[0].animate({ r: 5 }, 500, "bounce");
25346                     this.label[1].attr({ "font-weight": 400 });
25347                 }
25348             };
25349
25350         switch(graphtype){
25351             case 'bar':
25352                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25353                 break;
25354             case 'hbar':
25355                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25356                 break;
25357             case 'pie':
25358 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25359 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25360 //            
25361                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25362                 
25363                 break;
25364
25365         }
25366         
25367         if(this.title){
25368             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25369         }
25370         
25371     },
25372     
25373     setTitle: function(o)
25374     {
25375         this.title = o;
25376     },
25377     
25378     initEvents: function() {
25379         
25380         if(!this.href){
25381             this.el.on('click', this.onClick, this);
25382         }
25383     },
25384     
25385     onClick : function(e)
25386     {
25387         Roo.log('img onclick');
25388         this.fireEvent('click', this, e);
25389     }
25390    
25391 });
25392
25393  
25394 /*
25395  * - LGPL
25396  *
25397  * numberBox
25398  * 
25399  */
25400 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25401
25402 /**
25403  * @class Roo.bootstrap.dash.NumberBox
25404  * @extends Roo.bootstrap.Component
25405  * Bootstrap NumberBox class
25406  * @cfg {String} headline Box headline
25407  * @cfg {String} content Box content
25408  * @cfg {String} icon Box icon
25409  * @cfg {String} footer Footer text
25410  * @cfg {String} fhref Footer href
25411  * 
25412  * @constructor
25413  * Create a new NumberBox
25414  * @param {Object} config The config object
25415  */
25416
25417
25418 Roo.bootstrap.dash.NumberBox = function(config){
25419     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25420     
25421 };
25422
25423 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25424     
25425     headline : '',
25426     content : '',
25427     icon : '',
25428     footer : '',
25429     fhref : '',
25430     ficon : '',
25431     
25432     getAutoCreate : function(){
25433         
25434         var cfg = {
25435             tag : 'div',
25436             cls : 'small-box ',
25437             cn : [
25438                 {
25439                     tag : 'div',
25440                     cls : 'inner',
25441                     cn :[
25442                         {
25443                             tag : 'h3',
25444                             cls : 'roo-headline',
25445                             html : this.headline
25446                         },
25447                         {
25448                             tag : 'p',
25449                             cls : 'roo-content',
25450                             html : this.content
25451                         }
25452                     ]
25453                 }
25454             ]
25455         };
25456         
25457         if(this.icon){
25458             cfg.cn.push({
25459                 tag : 'div',
25460                 cls : 'icon',
25461                 cn :[
25462                     {
25463                         tag : 'i',
25464                         cls : 'ion ' + this.icon
25465                     }
25466                 ]
25467             });
25468         }
25469         
25470         if(this.footer){
25471             var footer = {
25472                 tag : 'a',
25473                 cls : 'small-box-footer',
25474                 href : this.fhref || '#',
25475                 html : this.footer
25476             };
25477             
25478             cfg.cn.push(footer);
25479             
25480         }
25481         
25482         return  cfg;
25483     },
25484
25485     onRender : function(ct,position){
25486         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25487
25488
25489        
25490                 
25491     },
25492
25493     setHeadline: function (value)
25494     {
25495         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25496     },
25497     
25498     setFooter: function (value, href)
25499     {
25500         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25501         
25502         if(href){
25503             this.el.select('a.small-box-footer',true).first().attr('href', href);
25504         }
25505         
25506     },
25507
25508     setContent: function (value)
25509     {
25510         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25511     },
25512
25513     initEvents: function() 
25514     {   
25515         
25516     }
25517     
25518 });
25519
25520  
25521 /*
25522  * - LGPL
25523  *
25524  * TabBox
25525  * 
25526  */
25527 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25528
25529 /**
25530  * @class Roo.bootstrap.dash.TabBox
25531  * @extends Roo.bootstrap.Component
25532  * Bootstrap TabBox class
25533  * @cfg {String} title Title of the TabBox
25534  * @cfg {String} icon Icon of the TabBox
25535  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25536  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25537  * 
25538  * @constructor
25539  * Create a new TabBox
25540  * @param {Object} config The config object
25541  */
25542
25543
25544 Roo.bootstrap.dash.TabBox = function(config){
25545     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25546     this.addEvents({
25547         // raw events
25548         /**
25549          * @event addpane
25550          * When a pane is added
25551          * @param {Roo.bootstrap.dash.TabPane} pane
25552          */
25553         "addpane" : true,
25554         /**
25555          * @event activatepane
25556          * When a pane is activated
25557          * @param {Roo.bootstrap.dash.TabPane} pane
25558          */
25559         "activatepane" : true
25560         
25561          
25562     });
25563     
25564     this.panes = [];
25565 };
25566
25567 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25568
25569     title : '',
25570     icon : false,
25571     showtabs : true,
25572     tabScrollable : false,
25573     
25574     getChildContainer : function()
25575     {
25576         return this.el.select('.tab-content', true).first();
25577     },
25578     
25579     getAutoCreate : function(){
25580         
25581         var header = {
25582             tag: 'li',
25583             cls: 'pull-left header',
25584             html: this.title,
25585             cn : []
25586         };
25587         
25588         if(this.icon){
25589             header.cn.push({
25590                 tag: 'i',
25591                 cls: 'fa ' + this.icon
25592             });
25593         }
25594         
25595         var h = {
25596             tag: 'ul',
25597             cls: 'nav nav-tabs pull-right',
25598             cn: [
25599                 header
25600             ]
25601         };
25602         
25603         if(this.tabScrollable){
25604             h = {
25605                 tag: 'div',
25606                 cls: 'tab-header',
25607                 cn: [
25608                     {
25609                         tag: 'ul',
25610                         cls: 'nav nav-tabs pull-right',
25611                         cn: [
25612                             header
25613                         ]
25614                     }
25615                 ]
25616             };
25617         }
25618         
25619         var cfg = {
25620             tag: 'div',
25621             cls: 'nav-tabs-custom',
25622             cn: [
25623                 h,
25624                 {
25625                     tag: 'div',
25626                     cls: 'tab-content no-padding',
25627                     cn: []
25628                 }
25629             ]
25630         };
25631
25632         return  cfg;
25633     },
25634     initEvents : function()
25635     {
25636         //Roo.log('add add pane handler');
25637         this.on('addpane', this.onAddPane, this);
25638     },
25639      /**
25640      * Updates the box title
25641      * @param {String} html to set the title to.
25642      */
25643     setTitle : function(value)
25644     {
25645         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25646     },
25647     onAddPane : function(pane)
25648     {
25649         this.panes.push(pane);
25650         //Roo.log('addpane');
25651         //Roo.log(pane);
25652         // tabs are rendere left to right..
25653         if(!this.showtabs){
25654             return;
25655         }
25656         
25657         var ctr = this.el.select('.nav-tabs', true).first();
25658          
25659          
25660         var existing = ctr.select('.nav-tab',true);
25661         var qty = existing.getCount();;
25662         
25663         
25664         var tab = ctr.createChild({
25665             tag : 'li',
25666             cls : 'nav-tab' + (qty ? '' : ' active'),
25667             cn : [
25668                 {
25669                     tag : 'a',
25670                     href:'#',
25671                     html : pane.title
25672                 }
25673             ]
25674         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25675         pane.tab = tab;
25676         
25677         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25678         if (!qty) {
25679             pane.el.addClass('active');
25680         }
25681         
25682                 
25683     },
25684     onTabClick : function(ev,un,ob,pane)
25685     {
25686         //Roo.log('tab - prev default');
25687         ev.preventDefault();
25688         
25689         
25690         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25691         pane.tab.addClass('active');
25692         //Roo.log(pane.title);
25693         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25694         // technically we should have a deactivate event.. but maybe add later.
25695         // and it should not de-activate the selected tab...
25696         this.fireEvent('activatepane', pane);
25697         pane.el.addClass('active');
25698         pane.fireEvent('activate');
25699         
25700         
25701     },
25702     
25703     getActivePane : function()
25704     {
25705         var r = false;
25706         Roo.each(this.panes, function(p) {
25707             if(p.el.hasClass('active')){
25708                 r = p;
25709                 return false;
25710             }
25711             
25712             return;
25713         });
25714         
25715         return r;
25716     }
25717     
25718     
25719 });
25720
25721  
25722 /*
25723  * - LGPL
25724  *
25725  * Tab pane
25726  * 
25727  */
25728 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25729 /**
25730  * @class Roo.bootstrap.TabPane
25731  * @extends Roo.bootstrap.Component
25732  * Bootstrap TabPane class
25733  * @cfg {Boolean} active (false | true) Default false
25734  * @cfg {String} title title of panel
25735
25736  * 
25737  * @constructor
25738  * Create a new TabPane
25739  * @param {Object} config The config object
25740  */
25741
25742 Roo.bootstrap.dash.TabPane = function(config){
25743     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25744     
25745     this.addEvents({
25746         // raw events
25747         /**
25748          * @event activate
25749          * When a pane is activated
25750          * @param {Roo.bootstrap.dash.TabPane} pane
25751          */
25752         "activate" : true
25753          
25754     });
25755 };
25756
25757 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25758     
25759     active : false,
25760     title : '',
25761     
25762     // the tabBox that this is attached to.
25763     tab : false,
25764      
25765     getAutoCreate : function() 
25766     {
25767         var cfg = {
25768             tag: 'div',
25769             cls: 'tab-pane'
25770         };
25771         
25772         if(this.active){
25773             cfg.cls += ' active';
25774         }
25775         
25776         return cfg;
25777     },
25778     initEvents  : function()
25779     {
25780         //Roo.log('trigger add pane handler');
25781         this.parent().fireEvent('addpane', this)
25782     },
25783     
25784      /**
25785      * Updates the tab title 
25786      * @param {String} html to set the title to.
25787      */
25788     setTitle: function(str)
25789     {
25790         if (!this.tab) {
25791             return;
25792         }
25793         this.title = str;
25794         this.tab.select('a', true).first().dom.innerHTML = str;
25795         
25796     }
25797     
25798     
25799     
25800 });
25801
25802  
25803
25804
25805  /*
25806  * - LGPL
25807  *
25808  * menu
25809  * 
25810  */
25811 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25812
25813 /**
25814  * @class Roo.bootstrap.menu.Menu
25815  * @extends Roo.bootstrap.Component
25816  * Bootstrap Menu class - container for Menu
25817  * @cfg {String} html Text of the menu
25818  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25819  * @cfg {String} icon Font awesome icon
25820  * @cfg {String} pos Menu align to (top | bottom) default bottom
25821  * 
25822  * 
25823  * @constructor
25824  * Create a new Menu
25825  * @param {Object} config The config object
25826  */
25827
25828
25829 Roo.bootstrap.menu.Menu = function(config){
25830     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25831     
25832     this.addEvents({
25833         /**
25834          * @event beforeshow
25835          * Fires before this menu is displayed
25836          * @param {Roo.bootstrap.menu.Menu} this
25837          */
25838         beforeshow : true,
25839         /**
25840          * @event beforehide
25841          * Fires before this menu is hidden
25842          * @param {Roo.bootstrap.menu.Menu} this
25843          */
25844         beforehide : true,
25845         /**
25846          * @event show
25847          * Fires after this menu is displayed
25848          * @param {Roo.bootstrap.menu.Menu} this
25849          */
25850         show : true,
25851         /**
25852          * @event hide
25853          * Fires after this menu is hidden
25854          * @param {Roo.bootstrap.menu.Menu} this
25855          */
25856         hide : true,
25857         /**
25858          * @event click
25859          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25860          * @param {Roo.bootstrap.menu.Menu} this
25861          * @param {Roo.EventObject} e
25862          */
25863         click : true
25864     });
25865     
25866 };
25867
25868 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25869     
25870     submenu : false,
25871     html : '',
25872     weight : 'default',
25873     icon : false,
25874     pos : 'bottom',
25875     
25876     
25877     getChildContainer : function() {
25878         if(this.isSubMenu){
25879             return this.el;
25880         }
25881         
25882         return this.el.select('ul.dropdown-menu', true).first();  
25883     },
25884     
25885     getAutoCreate : function()
25886     {
25887         var text = [
25888             {
25889                 tag : 'span',
25890                 cls : 'roo-menu-text',
25891                 html : this.html
25892             }
25893         ];
25894         
25895         if(this.icon){
25896             text.unshift({
25897                 tag : 'i',
25898                 cls : 'fa ' + this.icon
25899             })
25900         }
25901         
25902         
25903         var cfg = {
25904             tag : 'div',
25905             cls : 'btn-group',
25906             cn : [
25907                 {
25908                     tag : 'button',
25909                     cls : 'dropdown-button btn btn-' + this.weight,
25910                     cn : text
25911                 },
25912                 {
25913                     tag : 'button',
25914                     cls : 'dropdown-toggle btn btn-' + this.weight,
25915                     cn : [
25916                         {
25917                             tag : 'span',
25918                             cls : 'caret'
25919                         }
25920                     ]
25921                 },
25922                 {
25923                     tag : 'ul',
25924                     cls : 'dropdown-menu'
25925                 }
25926             ]
25927             
25928         };
25929         
25930         if(this.pos == 'top'){
25931             cfg.cls += ' dropup';
25932         }
25933         
25934         if(this.isSubMenu){
25935             cfg = {
25936                 tag : 'ul',
25937                 cls : 'dropdown-menu'
25938             }
25939         }
25940         
25941         return cfg;
25942     },
25943     
25944     onRender : function(ct, position)
25945     {
25946         this.isSubMenu = ct.hasClass('dropdown-submenu');
25947         
25948         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25949     },
25950     
25951     initEvents : function() 
25952     {
25953         if(this.isSubMenu){
25954             return;
25955         }
25956         
25957         this.hidden = true;
25958         
25959         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25960         this.triggerEl.on('click', this.onTriggerPress, this);
25961         
25962         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25963         this.buttonEl.on('click', this.onClick, this);
25964         
25965     },
25966     
25967     list : function()
25968     {
25969         if(this.isSubMenu){
25970             return this.el;
25971         }
25972         
25973         return this.el.select('ul.dropdown-menu', true).first();
25974     },
25975     
25976     onClick : function(e)
25977     {
25978         this.fireEvent("click", this, e);
25979     },
25980     
25981     onTriggerPress  : function(e)
25982     {   
25983         if (this.isVisible()) {
25984             this.hide();
25985         } else {
25986             this.show();
25987         }
25988     },
25989     
25990     isVisible : function(){
25991         return !this.hidden;
25992     },
25993     
25994     show : function()
25995     {
25996         this.fireEvent("beforeshow", this);
25997         
25998         this.hidden = false;
25999         this.el.addClass('open');
26000         
26001         Roo.get(document).on("mouseup", this.onMouseUp, this);
26002         
26003         this.fireEvent("show", this);
26004         
26005         
26006     },
26007     
26008     hide : function()
26009     {
26010         this.fireEvent("beforehide", this);
26011         
26012         this.hidden = true;
26013         this.el.removeClass('open');
26014         
26015         Roo.get(document).un("mouseup", this.onMouseUp);
26016         
26017         this.fireEvent("hide", this);
26018     },
26019     
26020     onMouseUp : function()
26021     {
26022         this.hide();
26023     }
26024     
26025 });
26026
26027  
26028  /*
26029  * - LGPL
26030  *
26031  * menu item
26032  * 
26033  */
26034 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26035
26036 /**
26037  * @class Roo.bootstrap.menu.Item
26038  * @extends Roo.bootstrap.Component
26039  * Bootstrap MenuItem class
26040  * @cfg {Boolean} submenu (true | false) default false
26041  * @cfg {String} html text of the item
26042  * @cfg {String} href the link
26043  * @cfg {Boolean} disable (true | false) default false
26044  * @cfg {Boolean} preventDefault (true | false) default true
26045  * @cfg {String} icon Font awesome icon
26046  * @cfg {String} pos Submenu align to (left | right) default right 
26047  * 
26048  * 
26049  * @constructor
26050  * Create a new Item
26051  * @param {Object} config The config object
26052  */
26053
26054
26055 Roo.bootstrap.menu.Item = function(config){
26056     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26057     this.addEvents({
26058         /**
26059          * @event mouseover
26060          * Fires when the mouse is hovering over this menu
26061          * @param {Roo.bootstrap.menu.Item} this
26062          * @param {Roo.EventObject} e
26063          */
26064         mouseover : true,
26065         /**
26066          * @event mouseout
26067          * Fires when the mouse exits this menu
26068          * @param {Roo.bootstrap.menu.Item} this
26069          * @param {Roo.EventObject} e
26070          */
26071         mouseout : true,
26072         // raw events
26073         /**
26074          * @event click
26075          * The raw click event for the entire grid.
26076          * @param {Roo.EventObject} e
26077          */
26078         click : true
26079     });
26080 };
26081
26082 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26083     
26084     submenu : false,
26085     href : '',
26086     html : '',
26087     preventDefault: true,
26088     disable : false,
26089     icon : false,
26090     pos : 'right',
26091     
26092     getAutoCreate : function()
26093     {
26094         var text = [
26095             {
26096                 tag : 'span',
26097                 cls : 'roo-menu-item-text',
26098                 html : this.html
26099             }
26100         ];
26101         
26102         if(this.icon){
26103             text.unshift({
26104                 tag : 'i',
26105                 cls : 'fa ' + this.icon
26106             })
26107         }
26108         
26109         var cfg = {
26110             tag : 'li',
26111             cn : [
26112                 {
26113                     tag : 'a',
26114                     href : this.href || '#',
26115                     cn : text
26116                 }
26117             ]
26118         };
26119         
26120         if(this.disable){
26121             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26122         }
26123         
26124         if(this.submenu){
26125             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26126             
26127             if(this.pos == 'left'){
26128                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26129             }
26130         }
26131         
26132         return cfg;
26133     },
26134     
26135     initEvents : function() 
26136     {
26137         this.el.on('mouseover', this.onMouseOver, this);
26138         this.el.on('mouseout', this.onMouseOut, this);
26139         
26140         this.el.select('a', true).first().on('click', this.onClick, this);
26141         
26142     },
26143     
26144     onClick : function(e)
26145     {
26146         if(this.preventDefault){
26147             e.preventDefault();
26148         }
26149         
26150         this.fireEvent("click", this, e);
26151     },
26152     
26153     onMouseOver : function(e)
26154     {
26155         if(this.submenu && this.pos == 'left'){
26156             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26157         }
26158         
26159         this.fireEvent("mouseover", this, e);
26160     },
26161     
26162     onMouseOut : function(e)
26163     {
26164         this.fireEvent("mouseout", this, e);
26165     }
26166 });
26167
26168  
26169
26170  /*
26171  * - LGPL
26172  *
26173  * menu separator
26174  * 
26175  */
26176 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26177
26178 /**
26179  * @class Roo.bootstrap.menu.Separator
26180  * @extends Roo.bootstrap.Component
26181  * Bootstrap Separator class
26182  * 
26183  * @constructor
26184  * Create a new Separator
26185  * @param {Object} config The config object
26186  */
26187
26188
26189 Roo.bootstrap.menu.Separator = function(config){
26190     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26191 };
26192
26193 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26194     
26195     getAutoCreate : function(){
26196         var cfg = {
26197             tag : 'li',
26198             cls: 'divider'
26199         };
26200         
26201         return cfg;
26202     }
26203    
26204 });
26205
26206  
26207
26208  /*
26209  * - LGPL
26210  *
26211  * Tooltip
26212  * 
26213  */
26214
26215 /**
26216  * @class Roo.bootstrap.Tooltip
26217  * Bootstrap Tooltip class
26218  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26219  * to determine which dom element triggers the tooltip.
26220  * 
26221  * It needs to add support for additional attributes like tooltip-position
26222  * 
26223  * @constructor
26224  * Create a new Toolti
26225  * @param {Object} config The config object
26226  */
26227
26228 Roo.bootstrap.Tooltip = function(config){
26229     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26230     
26231     this.alignment = Roo.bootstrap.Tooltip.alignment;
26232     
26233     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26234         this.alignment = config.alignment;
26235     }
26236     
26237 };
26238
26239 Roo.apply(Roo.bootstrap.Tooltip, {
26240     /**
26241      * @function init initialize tooltip monitoring.
26242      * @static
26243      */
26244     currentEl : false,
26245     currentTip : false,
26246     currentRegion : false,
26247     
26248     //  init : delay?
26249     
26250     init : function()
26251     {
26252         Roo.get(document).on('mouseover', this.enter ,this);
26253         Roo.get(document).on('mouseout', this.leave, this);
26254          
26255         
26256         this.currentTip = new Roo.bootstrap.Tooltip();
26257     },
26258     
26259     enter : function(ev)
26260     {
26261         var dom = ev.getTarget();
26262         
26263         //Roo.log(['enter',dom]);
26264         var el = Roo.fly(dom);
26265         if (this.currentEl) {
26266             //Roo.log(dom);
26267             //Roo.log(this.currentEl);
26268             //Roo.log(this.currentEl.contains(dom));
26269             if (this.currentEl == el) {
26270                 return;
26271             }
26272             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26273                 return;
26274             }
26275
26276         }
26277         
26278         if (this.currentTip.el) {
26279             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26280         }    
26281         //Roo.log(ev);
26282         
26283         if(!el || el.dom == document){
26284             return;
26285         }
26286         
26287         var bindEl = el;
26288         
26289         // you can not look for children, as if el is the body.. then everythign is the child..
26290         if (!el.attr('tooltip')) { //
26291             if (!el.select("[tooltip]").elements.length) {
26292                 return;
26293             }
26294             // is the mouse over this child...?
26295             bindEl = el.select("[tooltip]").first();
26296             var xy = ev.getXY();
26297             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26298                 //Roo.log("not in region.");
26299                 return;
26300             }
26301             //Roo.log("child element over..");
26302             
26303         }
26304         this.currentEl = bindEl;
26305         this.currentTip.bind(bindEl);
26306         this.currentRegion = Roo.lib.Region.getRegion(dom);
26307         this.currentTip.enter();
26308         
26309     },
26310     leave : function(ev)
26311     {
26312         var dom = ev.getTarget();
26313         //Roo.log(['leave',dom]);
26314         if (!this.currentEl) {
26315             return;
26316         }
26317         
26318         
26319         if (dom != this.currentEl.dom) {
26320             return;
26321         }
26322         var xy = ev.getXY();
26323         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26324             return;
26325         }
26326         // only activate leave if mouse cursor is outside... bounding box..
26327         
26328         
26329         
26330         
26331         if (this.currentTip) {
26332             this.currentTip.leave();
26333         }
26334         //Roo.log('clear currentEl');
26335         this.currentEl = false;
26336         
26337         
26338     },
26339     alignment : {
26340         'left' : ['r-l', [-2,0], 'right'],
26341         'right' : ['l-r', [2,0], 'left'],
26342         'bottom' : ['t-b', [0,2], 'top'],
26343         'top' : [ 'b-t', [0,-2], 'bottom']
26344     }
26345     
26346 });
26347
26348
26349 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26350     
26351     
26352     bindEl : false,
26353     
26354     delay : null, // can be { show : 300 , hide: 500}
26355     
26356     timeout : null,
26357     
26358     hoverState : null, //???
26359     
26360     placement : 'bottom', 
26361     
26362     alignment : false,
26363     
26364     getAutoCreate : function(){
26365     
26366         var cfg = {
26367            cls : 'tooltip',
26368            role : 'tooltip',
26369            cn : [
26370                 {
26371                     cls : 'tooltip-arrow'
26372                 },
26373                 {
26374                     cls : 'tooltip-inner'
26375                 }
26376            ]
26377         };
26378         
26379         return cfg;
26380     },
26381     bind : function(el)
26382     {
26383         this.bindEl = el;
26384     },
26385       
26386     
26387     enter : function () {
26388        
26389         if (this.timeout != null) {
26390             clearTimeout(this.timeout);
26391         }
26392         
26393         this.hoverState = 'in';
26394          //Roo.log("enter - show");
26395         if (!this.delay || !this.delay.show) {
26396             this.show();
26397             return;
26398         }
26399         var _t = this;
26400         this.timeout = setTimeout(function () {
26401             if (_t.hoverState == 'in') {
26402                 _t.show();
26403             }
26404         }, this.delay.show);
26405     },
26406     leave : function()
26407     {
26408         clearTimeout(this.timeout);
26409     
26410         this.hoverState = 'out';
26411          if (!this.delay || !this.delay.hide) {
26412             this.hide();
26413             return;
26414         }
26415        
26416         var _t = this;
26417         this.timeout = setTimeout(function () {
26418             //Roo.log("leave - timeout");
26419             
26420             if (_t.hoverState == 'out') {
26421                 _t.hide();
26422                 Roo.bootstrap.Tooltip.currentEl = false;
26423             }
26424         }, delay);
26425     },
26426     
26427     show : function (msg)
26428     {
26429         if (!this.el) {
26430             this.render(document.body);
26431         }
26432         // set content.
26433         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26434         
26435         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26436         
26437         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26438         
26439         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26440         
26441         var placement = typeof this.placement == 'function' ?
26442             this.placement.call(this, this.el, on_el) :
26443             this.placement;
26444             
26445         var autoToken = /\s?auto?\s?/i;
26446         var autoPlace = autoToken.test(placement);
26447         if (autoPlace) {
26448             placement = placement.replace(autoToken, '') || 'top';
26449         }
26450         
26451         //this.el.detach()
26452         //this.el.setXY([0,0]);
26453         this.el.show();
26454         //this.el.dom.style.display='block';
26455         
26456         //this.el.appendTo(on_el);
26457         
26458         var p = this.getPosition();
26459         var box = this.el.getBox();
26460         
26461         if (autoPlace) {
26462             // fixme..
26463         }
26464         
26465         var align = this.alignment[placement];
26466         
26467         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26468         
26469         if(placement == 'top' || placement == 'bottom'){
26470             if(xy[0] < 0){
26471                 placement = 'right';
26472             }
26473             
26474             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26475                 placement = 'left';
26476             }
26477             
26478             var scroll = Roo.select('body', true).first().getScroll();
26479             
26480             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26481                 placement = 'top';
26482             }
26483             
26484             align = this.alignment[placement];
26485         }
26486         
26487         this.el.alignTo(this.bindEl, align[0],align[1]);
26488         //var arrow = this.el.select('.arrow',true).first();
26489         //arrow.set(align[2], 
26490         
26491         this.el.addClass(placement);
26492         
26493         this.el.addClass('in fade');
26494         
26495         this.hoverState = null;
26496         
26497         if (this.el.hasClass('fade')) {
26498             // fade it?
26499         }
26500         
26501     },
26502     hide : function()
26503     {
26504          
26505         if (!this.el) {
26506             return;
26507         }
26508         //this.el.setXY([0,0]);
26509         this.el.removeClass('in');
26510         //this.el.hide();
26511         
26512     }
26513     
26514 });
26515  
26516
26517  /*
26518  * - LGPL
26519  *
26520  * Location Picker
26521  * 
26522  */
26523
26524 /**
26525  * @class Roo.bootstrap.LocationPicker
26526  * @extends Roo.bootstrap.Component
26527  * Bootstrap LocationPicker class
26528  * @cfg {Number} latitude Position when init default 0
26529  * @cfg {Number} longitude Position when init default 0
26530  * @cfg {Number} zoom default 15
26531  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26532  * @cfg {Boolean} mapTypeControl default false
26533  * @cfg {Boolean} disableDoubleClickZoom default false
26534  * @cfg {Boolean} scrollwheel default true
26535  * @cfg {Boolean} streetViewControl default false
26536  * @cfg {Number} radius default 0
26537  * @cfg {String} locationName
26538  * @cfg {Boolean} draggable default true
26539  * @cfg {Boolean} enableAutocomplete default false
26540  * @cfg {Boolean} enableReverseGeocode default true
26541  * @cfg {String} markerTitle
26542  * 
26543  * @constructor
26544  * Create a new LocationPicker
26545  * @param {Object} config The config object
26546  */
26547
26548
26549 Roo.bootstrap.LocationPicker = function(config){
26550     
26551     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26552     
26553     this.addEvents({
26554         /**
26555          * @event initial
26556          * Fires when the picker initialized.
26557          * @param {Roo.bootstrap.LocationPicker} this
26558          * @param {Google Location} location
26559          */
26560         initial : true,
26561         /**
26562          * @event positionchanged
26563          * Fires when the picker position changed.
26564          * @param {Roo.bootstrap.LocationPicker} this
26565          * @param {Google Location} location
26566          */
26567         positionchanged : true,
26568         /**
26569          * @event resize
26570          * Fires when the map resize.
26571          * @param {Roo.bootstrap.LocationPicker} this
26572          */
26573         resize : true,
26574         /**
26575          * @event show
26576          * Fires when the map show.
26577          * @param {Roo.bootstrap.LocationPicker} this
26578          */
26579         show : true,
26580         /**
26581          * @event hide
26582          * Fires when the map hide.
26583          * @param {Roo.bootstrap.LocationPicker} this
26584          */
26585         hide : true,
26586         /**
26587          * @event mapClick
26588          * Fires when click the map.
26589          * @param {Roo.bootstrap.LocationPicker} this
26590          * @param {Map event} e
26591          */
26592         mapClick : true,
26593         /**
26594          * @event mapRightClick
26595          * Fires when right click the map.
26596          * @param {Roo.bootstrap.LocationPicker} this
26597          * @param {Map event} e
26598          */
26599         mapRightClick : true,
26600         /**
26601          * @event markerClick
26602          * Fires when click the marker.
26603          * @param {Roo.bootstrap.LocationPicker} this
26604          * @param {Map event} e
26605          */
26606         markerClick : true,
26607         /**
26608          * @event markerRightClick
26609          * Fires when right click the marker.
26610          * @param {Roo.bootstrap.LocationPicker} this
26611          * @param {Map event} e
26612          */
26613         markerRightClick : true,
26614         /**
26615          * @event OverlayViewDraw
26616          * Fires when OverlayView Draw
26617          * @param {Roo.bootstrap.LocationPicker} this
26618          */
26619         OverlayViewDraw : true,
26620         /**
26621          * @event OverlayViewOnAdd
26622          * Fires when OverlayView Draw
26623          * @param {Roo.bootstrap.LocationPicker} this
26624          */
26625         OverlayViewOnAdd : true,
26626         /**
26627          * @event OverlayViewOnRemove
26628          * Fires when OverlayView Draw
26629          * @param {Roo.bootstrap.LocationPicker} this
26630          */
26631         OverlayViewOnRemove : true,
26632         /**
26633          * @event OverlayViewShow
26634          * Fires when OverlayView Draw
26635          * @param {Roo.bootstrap.LocationPicker} this
26636          * @param {Pixel} cpx
26637          */
26638         OverlayViewShow : true,
26639         /**
26640          * @event OverlayViewHide
26641          * Fires when OverlayView Draw
26642          * @param {Roo.bootstrap.LocationPicker} this
26643          */
26644         OverlayViewHide : true,
26645         /**
26646          * @event loadexception
26647          * Fires when load google lib failed.
26648          * @param {Roo.bootstrap.LocationPicker} this
26649          */
26650         loadexception : true
26651     });
26652         
26653 };
26654
26655 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26656     
26657     gMapContext: false,
26658     
26659     latitude: 0,
26660     longitude: 0,
26661     zoom: 15,
26662     mapTypeId: false,
26663     mapTypeControl: false,
26664     disableDoubleClickZoom: false,
26665     scrollwheel: true,
26666     streetViewControl: false,
26667     radius: 0,
26668     locationName: '',
26669     draggable: true,
26670     enableAutocomplete: false,
26671     enableReverseGeocode: true,
26672     markerTitle: '',
26673     
26674     getAutoCreate: function()
26675     {
26676
26677         var cfg = {
26678             tag: 'div',
26679             cls: 'roo-location-picker'
26680         };
26681         
26682         return cfg
26683     },
26684     
26685     initEvents: function(ct, position)
26686     {       
26687         if(!this.el.getWidth() || this.isApplied()){
26688             return;
26689         }
26690         
26691         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26692         
26693         this.initial();
26694     },
26695     
26696     initial: function()
26697     {
26698         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26699             this.fireEvent('loadexception', this);
26700             return;
26701         }
26702         
26703         if(!this.mapTypeId){
26704             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26705         }
26706         
26707         this.gMapContext = this.GMapContext();
26708         
26709         this.initOverlayView();
26710         
26711         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26712         
26713         var _this = this;
26714                 
26715         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26716             _this.setPosition(_this.gMapContext.marker.position);
26717         });
26718         
26719         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26720             _this.fireEvent('mapClick', this, event);
26721             
26722         });
26723
26724         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26725             _this.fireEvent('mapRightClick', this, event);
26726             
26727         });
26728         
26729         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26730             _this.fireEvent('markerClick', this, event);
26731             
26732         });
26733
26734         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26735             _this.fireEvent('markerRightClick', this, event);
26736             
26737         });
26738         
26739         this.setPosition(this.gMapContext.location);
26740         
26741         this.fireEvent('initial', this, this.gMapContext.location);
26742     },
26743     
26744     initOverlayView: function()
26745     {
26746         var _this = this;
26747         
26748         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26749             
26750             draw: function()
26751             {
26752                 _this.fireEvent('OverlayViewDraw', _this);
26753             },
26754             
26755             onAdd: function()
26756             {
26757                 _this.fireEvent('OverlayViewOnAdd', _this);
26758             },
26759             
26760             onRemove: function()
26761             {
26762                 _this.fireEvent('OverlayViewOnRemove', _this);
26763             },
26764             
26765             show: function(cpx)
26766             {
26767                 _this.fireEvent('OverlayViewShow', _this, cpx);
26768             },
26769             
26770             hide: function()
26771             {
26772                 _this.fireEvent('OverlayViewHide', _this);
26773             }
26774             
26775         });
26776     },
26777     
26778     fromLatLngToContainerPixel: function(event)
26779     {
26780         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26781     },
26782     
26783     isApplied: function() 
26784     {
26785         return this.getGmapContext() == false ? false : true;
26786     },
26787     
26788     getGmapContext: function() 
26789     {
26790         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26791     },
26792     
26793     GMapContext: function() 
26794     {
26795         var position = new google.maps.LatLng(this.latitude, this.longitude);
26796         
26797         var _map = new google.maps.Map(this.el.dom, {
26798             center: position,
26799             zoom: this.zoom,
26800             mapTypeId: this.mapTypeId,
26801             mapTypeControl: this.mapTypeControl,
26802             disableDoubleClickZoom: this.disableDoubleClickZoom,
26803             scrollwheel: this.scrollwheel,
26804             streetViewControl: this.streetViewControl,
26805             locationName: this.locationName,
26806             draggable: this.draggable,
26807             enableAutocomplete: this.enableAutocomplete,
26808             enableReverseGeocode: this.enableReverseGeocode
26809         });
26810         
26811         var _marker = new google.maps.Marker({
26812             position: position,
26813             map: _map,
26814             title: this.markerTitle,
26815             draggable: this.draggable
26816         });
26817         
26818         return {
26819             map: _map,
26820             marker: _marker,
26821             circle: null,
26822             location: position,
26823             radius: this.radius,
26824             locationName: this.locationName,
26825             addressComponents: {
26826                 formatted_address: null,
26827                 addressLine1: null,
26828                 addressLine2: null,
26829                 streetName: null,
26830                 streetNumber: null,
26831                 city: null,
26832                 district: null,
26833                 state: null,
26834                 stateOrProvince: null
26835             },
26836             settings: this,
26837             domContainer: this.el.dom,
26838             geodecoder: new google.maps.Geocoder()
26839         };
26840     },
26841     
26842     drawCircle: function(center, radius, options) 
26843     {
26844         if (this.gMapContext.circle != null) {
26845             this.gMapContext.circle.setMap(null);
26846         }
26847         if (radius > 0) {
26848             radius *= 1;
26849             options = Roo.apply({}, options, {
26850                 strokeColor: "#0000FF",
26851                 strokeOpacity: .35,
26852                 strokeWeight: 2,
26853                 fillColor: "#0000FF",
26854                 fillOpacity: .2
26855             });
26856             
26857             options.map = this.gMapContext.map;
26858             options.radius = radius;
26859             options.center = center;
26860             this.gMapContext.circle = new google.maps.Circle(options);
26861             return this.gMapContext.circle;
26862         }
26863         
26864         return null;
26865     },
26866     
26867     setPosition: function(location) 
26868     {
26869         this.gMapContext.location = location;
26870         this.gMapContext.marker.setPosition(location);
26871         this.gMapContext.map.panTo(location);
26872         this.drawCircle(location, this.gMapContext.radius, {});
26873         
26874         var _this = this;
26875         
26876         if (this.gMapContext.settings.enableReverseGeocode) {
26877             this.gMapContext.geodecoder.geocode({
26878                 latLng: this.gMapContext.location
26879             }, function(results, status) {
26880                 
26881                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26882                     _this.gMapContext.locationName = results[0].formatted_address;
26883                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26884                     
26885                     _this.fireEvent('positionchanged', this, location);
26886                 }
26887             });
26888             
26889             return;
26890         }
26891         
26892         this.fireEvent('positionchanged', this, location);
26893     },
26894     
26895     resize: function()
26896     {
26897         google.maps.event.trigger(this.gMapContext.map, "resize");
26898         
26899         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26900         
26901         this.fireEvent('resize', this);
26902     },
26903     
26904     setPositionByLatLng: function(latitude, longitude)
26905     {
26906         this.setPosition(new google.maps.LatLng(latitude, longitude));
26907     },
26908     
26909     getCurrentPosition: function() 
26910     {
26911         return {
26912             latitude: this.gMapContext.location.lat(),
26913             longitude: this.gMapContext.location.lng()
26914         };
26915     },
26916     
26917     getAddressName: function() 
26918     {
26919         return this.gMapContext.locationName;
26920     },
26921     
26922     getAddressComponents: function() 
26923     {
26924         return this.gMapContext.addressComponents;
26925     },
26926     
26927     address_component_from_google_geocode: function(address_components) 
26928     {
26929         var result = {};
26930         
26931         for (var i = 0; i < address_components.length; i++) {
26932             var component = address_components[i];
26933             if (component.types.indexOf("postal_code") >= 0) {
26934                 result.postalCode = component.short_name;
26935             } else if (component.types.indexOf("street_number") >= 0) {
26936                 result.streetNumber = component.short_name;
26937             } else if (component.types.indexOf("route") >= 0) {
26938                 result.streetName = component.short_name;
26939             } else if (component.types.indexOf("neighborhood") >= 0) {
26940                 result.city = component.short_name;
26941             } else if (component.types.indexOf("locality") >= 0) {
26942                 result.city = component.short_name;
26943             } else if (component.types.indexOf("sublocality") >= 0) {
26944                 result.district = component.short_name;
26945             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26946                 result.stateOrProvince = component.short_name;
26947             } else if (component.types.indexOf("country") >= 0) {
26948                 result.country = component.short_name;
26949             }
26950         }
26951         
26952         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26953         result.addressLine2 = "";
26954         return result;
26955     },
26956     
26957     setZoomLevel: function(zoom)
26958     {
26959         this.gMapContext.map.setZoom(zoom);
26960     },
26961     
26962     show: function()
26963     {
26964         if(!this.el){
26965             return;
26966         }
26967         
26968         this.el.show();
26969         
26970         this.resize();
26971         
26972         this.fireEvent('show', this);
26973     },
26974     
26975     hide: function()
26976     {
26977         if(!this.el){
26978             return;
26979         }
26980         
26981         this.el.hide();
26982         
26983         this.fireEvent('hide', this);
26984     }
26985     
26986 });
26987
26988 Roo.apply(Roo.bootstrap.LocationPicker, {
26989     
26990     OverlayView : function(map, options)
26991     {
26992         options = options || {};
26993         
26994         this.setMap(map);
26995     }
26996     
26997     
26998 });/*
26999  * - LGPL
27000  *
27001  * Alert
27002  * 
27003  */
27004
27005 /**
27006  * @class Roo.bootstrap.Alert
27007  * @extends Roo.bootstrap.Component
27008  * Bootstrap Alert class
27009  * @cfg {String} title The title of alert
27010  * @cfg {String} html The content of alert
27011  * @cfg {String} weight (  success | info | warning | danger )
27012  * @cfg {String} faicon font-awesomeicon
27013  * 
27014  * @constructor
27015  * Create a new alert
27016  * @param {Object} config The config object
27017  */
27018
27019
27020 Roo.bootstrap.Alert = function(config){
27021     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27022     
27023 };
27024
27025 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27026     
27027     title: '',
27028     html: '',
27029     weight: false,
27030     faicon: false,
27031     
27032     getAutoCreate : function()
27033     {
27034         
27035         var cfg = {
27036             tag : 'div',
27037             cls : 'alert',
27038             cn : [
27039                 {
27040                     tag : 'i',
27041                     cls : 'roo-alert-icon'
27042                     
27043                 },
27044                 {
27045                     tag : 'b',
27046                     cls : 'roo-alert-title',
27047                     html : this.title
27048                 },
27049                 {
27050                     tag : 'span',
27051                     cls : 'roo-alert-text',
27052                     html : this.html
27053                 }
27054             ]
27055         };
27056         
27057         if(this.faicon){
27058             cfg.cn[0].cls += ' fa ' + this.faicon;
27059         }
27060         
27061         if(this.weight){
27062             cfg.cls += ' alert-' + this.weight;
27063         }
27064         
27065         return cfg;
27066     },
27067     
27068     initEvents: function() 
27069     {
27070         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27071     },
27072     
27073     setTitle : function(str)
27074     {
27075         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27076     },
27077     
27078     setText : function(str)
27079     {
27080         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27081     },
27082     
27083     setWeight : function(weight)
27084     {
27085         if(this.weight){
27086             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27087         }
27088         
27089         this.weight = weight;
27090         
27091         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27092     },
27093     
27094     setIcon : function(icon)
27095     {
27096         if(this.faicon){
27097             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27098         }
27099         
27100         this.faicon = icon;
27101         
27102         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27103     },
27104     
27105     hide: function() 
27106     {
27107         this.el.hide();   
27108     },
27109     
27110     show: function() 
27111     {  
27112         this.el.show();   
27113     }
27114     
27115 });
27116
27117  
27118 /*
27119 * Licence: LGPL
27120 */
27121
27122 /**
27123  * @class Roo.bootstrap.UploadCropbox
27124  * @extends Roo.bootstrap.Component
27125  * Bootstrap UploadCropbox class
27126  * @cfg {String} emptyText show when image has been loaded
27127  * @cfg {String} rotateNotify show when image too small to rotate
27128  * @cfg {Number} errorTimeout default 3000
27129  * @cfg {Number} minWidth default 300
27130  * @cfg {Number} minHeight default 300
27131  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27132  * @cfg {Boolean} isDocument (true|false) default false
27133  * @cfg {String} url action url
27134  * @cfg {String} paramName default 'imageUpload'
27135  * @cfg {String} method default POST
27136  * @cfg {Boolean} loadMask (true|false) default true
27137  * @cfg {Boolean} loadingText default 'Loading...'
27138  * 
27139  * @constructor
27140  * Create a new UploadCropbox
27141  * @param {Object} config The config object
27142  */
27143
27144 Roo.bootstrap.UploadCropbox = function(config){
27145     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27146     
27147     this.addEvents({
27148         /**
27149          * @event beforeselectfile
27150          * Fire before select file
27151          * @param {Roo.bootstrap.UploadCropbox} this
27152          */
27153         "beforeselectfile" : true,
27154         /**
27155          * @event initial
27156          * Fire after initEvent
27157          * @param {Roo.bootstrap.UploadCropbox} this
27158          */
27159         "initial" : true,
27160         /**
27161          * @event crop
27162          * Fire after initEvent
27163          * @param {Roo.bootstrap.UploadCropbox} this
27164          * @param {String} data
27165          */
27166         "crop" : true,
27167         /**
27168          * @event prepare
27169          * Fire when preparing the file data
27170          * @param {Roo.bootstrap.UploadCropbox} this
27171          * @param {Object} file
27172          */
27173         "prepare" : true,
27174         /**
27175          * @event exception
27176          * Fire when get exception
27177          * @param {Roo.bootstrap.UploadCropbox} this
27178          * @param {XMLHttpRequest} xhr
27179          */
27180         "exception" : true,
27181         /**
27182          * @event beforeloadcanvas
27183          * Fire before load the canvas
27184          * @param {Roo.bootstrap.UploadCropbox} this
27185          * @param {String} src
27186          */
27187         "beforeloadcanvas" : true,
27188         /**
27189          * @event trash
27190          * Fire when trash image
27191          * @param {Roo.bootstrap.UploadCropbox} this
27192          */
27193         "trash" : true,
27194         /**
27195          * @event download
27196          * Fire when download the image
27197          * @param {Roo.bootstrap.UploadCropbox} this
27198          */
27199         "download" : true,
27200         /**
27201          * @event footerbuttonclick
27202          * Fire when footerbuttonclick
27203          * @param {Roo.bootstrap.UploadCropbox} this
27204          * @param {String} type
27205          */
27206         "footerbuttonclick" : true,
27207         /**
27208          * @event resize
27209          * Fire when resize
27210          * @param {Roo.bootstrap.UploadCropbox} this
27211          */
27212         "resize" : true,
27213         /**
27214          * @event rotate
27215          * Fire when rotate the image
27216          * @param {Roo.bootstrap.UploadCropbox} this
27217          * @param {String} pos
27218          */
27219         "rotate" : true,
27220         /**
27221          * @event inspect
27222          * Fire when inspect the file
27223          * @param {Roo.bootstrap.UploadCropbox} this
27224          * @param {Object} file
27225          */
27226         "inspect" : true,
27227         /**
27228          * @event upload
27229          * Fire when xhr upload the file
27230          * @param {Roo.bootstrap.UploadCropbox} this
27231          * @param {Object} data
27232          */
27233         "upload" : true,
27234         /**
27235          * @event arrange
27236          * Fire when arrange the file data
27237          * @param {Roo.bootstrap.UploadCropbox} this
27238          * @param {Object} formData
27239          */
27240         "arrange" : true
27241     });
27242     
27243     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27244 };
27245
27246 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27247     
27248     emptyText : 'Click to upload image',
27249     rotateNotify : 'Image is too small to rotate',
27250     errorTimeout : 3000,
27251     scale : 0,
27252     baseScale : 1,
27253     rotate : 0,
27254     dragable : false,
27255     pinching : false,
27256     mouseX : 0,
27257     mouseY : 0,
27258     cropData : false,
27259     minWidth : 300,
27260     minHeight : 300,
27261     file : false,
27262     exif : {},
27263     baseRotate : 1,
27264     cropType : 'image/jpeg',
27265     buttons : false,
27266     canvasLoaded : false,
27267     isDocument : false,
27268     method : 'POST',
27269     paramName : 'imageUpload',
27270     loadMask : true,
27271     loadingText : 'Loading...',
27272     maskEl : false,
27273     
27274     getAutoCreate : function()
27275     {
27276         var cfg = {
27277             tag : 'div',
27278             cls : 'roo-upload-cropbox',
27279             cn : [
27280                 {
27281                     tag : 'input',
27282                     cls : 'roo-upload-cropbox-selector',
27283                     type : 'file'
27284                 },
27285                 {
27286                     tag : 'div',
27287                     cls : 'roo-upload-cropbox-body',
27288                     style : 'cursor:pointer',
27289                     cn : [
27290                         {
27291                             tag : 'div',
27292                             cls : 'roo-upload-cropbox-preview'
27293                         },
27294                         {
27295                             tag : 'div',
27296                             cls : 'roo-upload-cropbox-thumb'
27297                         },
27298                         {
27299                             tag : 'div',
27300                             cls : 'roo-upload-cropbox-empty-notify',
27301                             html : this.emptyText
27302                         },
27303                         {
27304                             tag : 'div',
27305                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27306                             html : this.rotateNotify
27307                         }
27308                     ]
27309                 },
27310                 {
27311                     tag : 'div',
27312                     cls : 'roo-upload-cropbox-footer',
27313                     cn : {
27314                         tag : 'div',
27315                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27316                         cn : []
27317                     }
27318                 }
27319             ]
27320         };
27321         
27322         return cfg;
27323     },
27324     
27325     onRender : function(ct, position)
27326     {
27327         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27328         
27329         if (this.buttons.length) {
27330             
27331             Roo.each(this.buttons, function(bb) {
27332                 
27333                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27334                 
27335                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27336                 
27337             }, this);
27338         }
27339         
27340         if(this.loadMask){
27341             this.maskEl = this.el;
27342         }
27343     },
27344     
27345     initEvents : function()
27346     {
27347         this.urlAPI = (window.createObjectURL && window) || 
27348                                 (window.URL && URL.revokeObjectURL && URL) || 
27349                                 (window.webkitURL && webkitURL);
27350                         
27351         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27352         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27353         
27354         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27355         this.selectorEl.hide();
27356         
27357         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27358         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27359         
27360         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27361         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27362         this.thumbEl.hide();
27363         
27364         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27365         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27366         
27367         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27368         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27369         this.errorEl.hide();
27370         
27371         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27372         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27373         this.footerEl.hide();
27374         
27375         this.setThumbBoxSize();
27376         
27377         this.bind();
27378         
27379         this.resize();
27380         
27381         this.fireEvent('initial', this);
27382     },
27383
27384     bind : function()
27385     {
27386         var _this = this;
27387         
27388         window.addEventListener("resize", function() { _this.resize(); } );
27389         
27390         this.bodyEl.on('click', this.beforeSelectFile, this);
27391         
27392         if(Roo.isTouch){
27393             this.bodyEl.on('touchstart', this.onTouchStart, this);
27394             this.bodyEl.on('touchmove', this.onTouchMove, this);
27395             this.bodyEl.on('touchend', this.onTouchEnd, this);
27396         }
27397         
27398         if(!Roo.isTouch){
27399             this.bodyEl.on('mousedown', this.onMouseDown, this);
27400             this.bodyEl.on('mousemove', this.onMouseMove, this);
27401             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27402             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27403             Roo.get(document).on('mouseup', this.onMouseUp, this);
27404         }
27405         
27406         this.selectorEl.on('change', this.onFileSelected, this);
27407     },
27408     
27409     reset : function()
27410     {    
27411         this.scale = 0;
27412         this.baseScale = 1;
27413         this.rotate = 0;
27414         this.baseRotate = 1;
27415         this.dragable = false;
27416         this.pinching = false;
27417         this.mouseX = 0;
27418         this.mouseY = 0;
27419         this.cropData = false;
27420         this.notifyEl.dom.innerHTML = this.emptyText;
27421         
27422         this.selectorEl.dom.value = '';
27423         
27424     },
27425     
27426     resize : function()
27427     {
27428         if(this.fireEvent('resize', this) != false){
27429             this.setThumbBoxPosition();
27430             this.setCanvasPosition();
27431         }
27432     },
27433     
27434     onFooterButtonClick : function(e, el, o, type)
27435     {
27436         switch (type) {
27437             case 'rotate-left' :
27438                 this.onRotateLeft(e);
27439                 break;
27440             case 'rotate-right' :
27441                 this.onRotateRight(e);
27442                 break;
27443             case 'picture' :
27444                 this.beforeSelectFile(e);
27445                 break;
27446             case 'trash' :
27447                 this.trash(e);
27448                 break;
27449             case 'crop' :
27450                 this.crop(e);
27451                 break;
27452             case 'download' :
27453                 this.download(e);
27454                 break;
27455             default :
27456                 break;
27457         }
27458         
27459         this.fireEvent('footerbuttonclick', this, type);
27460     },
27461     
27462     beforeSelectFile : function(e)
27463     {
27464         e.preventDefault();
27465         
27466         if(this.fireEvent('beforeselectfile', this) != false){
27467             this.selectorEl.dom.click();
27468         }
27469     },
27470     
27471     onFileSelected : function(e)
27472     {
27473         e.preventDefault();
27474         
27475         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27476             return;
27477         }
27478         
27479         var file = this.selectorEl.dom.files[0];
27480         
27481         if(this.fireEvent('inspect', this, file) != false){
27482             this.prepare(file);
27483         }
27484         
27485     },
27486     
27487     trash : function(e)
27488     {
27489         this.fireEvent('trash', this);
27490     },
27491     
27492     download : function(e)
27493     {
27494         this.fireEvent('download', this);
27495     },
27496     
27497     loadCanvas : function(src)
27498     {   
27499         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27500             
27501             this.reset();
27502             
27503             this.imageEl = document.createElement('img');
27504             
27505             var _this = this;
27506             
27507             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27508             
27509             this.imageEl.src = src;
27510         }
27511     },
27512     
27513     onLoadCanvas : function()
27514     {   
27515         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27516         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27517         
27518         this.bodyEl.un('click', this.beforeSelectFile, this);
27519         
27520         this.notifyEl.hide();
27521         this.thumbEl.show();
27522         this.footerEl.show();
27523         
27524         this.baseRotateLevel();
27525         
27526         if(this.isDocument){
27527             this.setThumbBoxSize();
27528         }
27529         
27530         this.setThumbBoxPosition();
27531         
27532         this.baseScaleLevel();
27533         
27534         this.draw();
27535         
27536         this.resize();
27537         
27538         this.canvasLoaded = true;
27539         
27540         if(this.loadMask){
27541             this.maskEl.unmask();
27542         }
27543         
27544     },
27545     
27546     setCanvasPosition : function()
27547     {   
27548         if(!this.canvasEl){
27549             return;
27550         }
27551         
27552         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27553         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27554         
27555         this.previewEl.setLeft(pw);
27556         this.previewEl.setTop(ph);
27557         
27558     },
27559     
27560     onMouseDown : function(e)
27561     {   
27562         e.stopEvent();
27563         
27564         this.dragable = true;
27565         this.pinching = false;
27566         
27567         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27568             this.dragable = false;
27569             return;
27570         }
27571         
27572         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27573         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27574         
27575     },
27576     
27577     onMouseMove : function(e)
27578     {   
27579         e.stopEvent();
27580         
27581         if(!this.canvasLoaded){
27582             return;
27583         }
27584         
27585         if (!this.dragable){
27586             return;
27587         }
27588         
27589         var minX = Math.ceil(this.thumbEl.getLeft(true));
27590         var minY = Math.ceil(this.thumbEl.getTop(true));
27591         
27592         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27593         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27594         
27595         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27596         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27597         
27598         x = x - this.mouseX;
27599         y = y - this.mouseY;
27600         
27601         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27602         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27603         
27604         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27605         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27606         
27607         this.previewEl.setLeft(bgX);
27608         this.previewEl.setTop(bgY);
27609         
27610         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27611         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27612     },
27613     
27614     onMouseUp : function(e)
27615     {   
27616         e.stopEvent();
27617         
27618         this.dragable = false;
27619     },
27620     
27621     onMouseWheel : function(e)
27622     {   
27623         e.stopEvent();
27624         
27625         this.startScale = this.scale;
27626         
27627         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27628         
27629         if(!this.zoomable()){
27630             this.scale = this.startScale;
27631             return;
27632         }
27633         
27634         this.draw();
27635         
27636         return;
27637     },
27638     
27639     zoomable : function()
27640     {
27641         var minScale = this.thumbEl.getWidth() / this.minWidth;
27642         
27643         if(this.minWidth < this.minHeight){
27644             minScale = this.thumbEl.getHeight() / this.minHeight;
27645         }
27646         
27647         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27648         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27649         
27650         if(
27651                 this.isDocument &&
27652                 (this.rotate == 0 || this.rotate == 180) && 
27653                 (
27654                     width > this.imageEl.OriginWidth || 
27655                     height > this.imageEl.OriginHeight ||
27656                     (width < this.minWidth && height < this.minHeight)
27657                 )
27658         ){
27659             return false;
27660         }
27661         
27662         if(
27663                 this.isDocument &&
27664                 (this.rotate == 90 || this.rotate == 270) && 
27665                 (
27666                     width > this.imageEl.OriginWidth || 
27667                     height > this.imageEl.OriginHeight ||
27668                     (width < this.minHeight && height < this.minWidth)
27669                 )
27670         ){
27671             return false;
27672         }
27673         
27674         if(
27675                 !this.isDocument &&
27676                 (this.rotate == 0 || this.rotate == 180) && 
27677                 (
27678                     width < this.minWidth || 
27679                     width > this.imageEl.OriginWidth || 
27680                     height < this.minHeight || 
27681                     height > this.imageEl.OriginHeight
27682                 )
27683         ){
27684             return false;
27685         }
27686         
27687         if(
27688                 !this.isDocument &&
27689                 (this.rotate == 90 || this.rotate == 270) && 
27690                 (
27691                     width < this.minHeight || 
27692                     width > this.imageEl.OriginWidth || 
27693                     height < this.minWidth || 
27694                     height > this.imageEl.OriginHeight
27695                 )
27696         ){
27697             return false;
27698         }
27699         
27700         return true;
27701         
27702     },
27703     
27704     onRotateLeft : function(e)
27705     {   
27706         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27707             
27708             var minScale = this.thumbEl.getWidth() / this.minWidth;
27709             
27710             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27711             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27712             
27713             this.startScale = this.scale;
27714             
27715             while (this.getScaleLevel() < minScale){
27716             
27717                 this.scale = this.scale + 1;
27718                 
27719                 if(!this.zoomable()){
27720                     break;
27721                 }
27722                 
27723                 if(
27724                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27725                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27726                 ){
27727                     continue;
27728                 }
27729                 
27730                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27731
27732                 this.draw();
27733                 
27734                 return;
27735             }
27736             
27737             this.scale = this.startScale;
27738             
27739             this.onRotateFail();
27740             
27741             return false;
27742         }
27743         
27744         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27745
27746         if(this.isDocument){
27747             this.setThumbBoxSize();
27748             this.setThumbBoxPosition();
27749             this.setCanvasPosition();
27750         }
27751         
27752         this.draw();
27753         
27754         this.fireEvent('rotate', this, 'left');
27755         
27756     },
27757     
27758     onRotateRight : function(e)
27759     {
27760         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27761             
27762             var minScale = this.thumbEl.getWidth() / this.minWidth;
27763         
27764             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27765             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27766             
27767             this.startScale = this.scale;
27768             
27769             while (this.getScaleLevel() < minScale){
27770             
27771                 this.scale = this.scale + 1;
27772                 
27773                 if(!this.zoomable()){
27774                     break;
27775                 }
27776                 
27777                 if(
27778                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27779                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27780                 ){
27781                     continue;
27782                 }
27783                 
27784                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27785
27786                 this.draw();
27787                 
27788                 return;
27789             }
27790             
27791             this.scale = this.startScale;
27792             
27793             this.onRotateFail();
27794             
27795             return false;
27796         }
27797         
27798         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27799
27800         if(this.isDocument){
27801             this.setThumbBoxSize();
27802             this.setThumbBoxPosition();
27803             this.setCanvasPosition();
27804         }
27805         
27806         this.draw();
27807         
27808         this.fireEvent('rotate', this, 'right');
27809     },
27810     
27811     onRotateFail : function()
27812     {
27813         this.errorEl.show(true);
27814         
27815         var _this = this;
27816         
27817         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27818     },
27819     
27820     draw : function()
27821     {
27822         this.previewEl.dom.innerHTML = '';
27823         
27824         var canvasEl = document.createElement("canvas");
27825         
27826         var contextEl = canvasEl.getContext("2d");
27827         
27828         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27829         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27830         var center = this.imageEl.OriginWidth / 2;
27831         
27832         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27833             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27834             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27835             center = this.imageEl.OriginHeight / 2;
27836         }
27837         
27838         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27839         
27840         contextEl.translate(center, center);
27841         contextEl.rotate(this.rotate * Math.PI / 180);
27842
27843         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27844         
27845         this.canvasEl = document.createElement("canvas");
27846         
27847         this.contextEl = this.canvasEl.getContext("2d");
27848         
27849         switch (this.rotate) {
27850             case 0 :
27851                 
27852                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27853                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27854                 
27855                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27856                 
27857                 break;
27858             case 90 : 
27859                 
27860                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27861                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27862                 
27863                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27864                     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);
27865                     break;
27866                 }
27867                 
27868                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27869                 
27870                 break;
27871             case 180 :
27872                 
27873                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27874                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27875                 
27876                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27877                     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);
27878                     break;
27879                 }
27880                 
27881                 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);
27882                 
27883                 break;
27884             case 270 :
27885                 
27886                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27887                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27888         
27889                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27890                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27891                     break;
27892                 }
27893                 
27894                 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);
27895                 
27896                 break;
27897             default : 
27898                 break;
27899         }
27900         
27901         this.previewEl.appendChild(this.canvasEl);
27902         
27903         this.setCanvasPosition();
27904     },
27905     
27906     crop : function()
27907     {
27908         if(!this.canvasLoaded){
27909             return;
27910         }
27911         
27912         var imageCanvas = document.createElement("canvas");
27913         
27914         var imageContext = imageCanvas.getContext("2d");
27915         
27916         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27917         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27918         
27919         var center = imageCanvas.width / 2;
27920         
27921         imageContext.translate(center, center);
27922         
27923         imageContext.rotate(this.rotate * Math.PI / 180);
27924         
27925         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27926         
27927         var canvas = document.createElement("canvas");
27928         
27929         var context = canvas.getContext("2d");
27930                 
27931         canvas.width = this.minWidth;
27932         canvas.height = this.minHeight;
27933
27934         switch (this.rotate) {
27935             case 0 :
27936                 
27937                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27938                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27939                 
27940                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27941                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27942                 
27943                 var targetWidth = this.minWidth - 2 * x;
27944                 var targetHeight = this.minHeight - 2 * y;
27945                 
27946                 var scale = 1;
27947                 
27948                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27949                     scale = targetWidth / width;
27950                 }
27951                 
27952                 if(x > 0 && y == 0){
27953                     scale = targetHeight / height;
27954                 }
27955                 
27956                 if(x > 0 && y > 0){
27957                     scale = targetWidth / width;
27958                     
27959                     if(width < height){
27960                         scale = targetHeight / height;
27961                     }
27962                 }
27963                 
27964                 context.scale(scale, scale);
27965                 
27966                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27967                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27968
27969                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27970                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27971
27972                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27973                 
27974                 break;
27975             case 90 : 
27976                 
27977                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27978                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27979                 
27980                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27981                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27982                 
27983                 var targetWidth = this.minWidth - 2 * x;
27984                 var targetHeight = this.minHeight - 2 * y;
27985                 
27986                 var scale = 1;
27987                 
27988                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27989                     scale = targetWidth / width;
27990                 }
27991                 
27992                 if(x > 0 && y == 0){
27993                     scale = targetHeight / height;
27994                 }
27995                 
27996                 if(x > 0 && y > 0){
27997                     scale = targetWidth / width;
27998                     
27999                     if(width < height){
28000                         scale = targetHeight / height;
28001                     }
28002                 }
28003                 
28004                 context.scale(scale, scale);
28005                 
28006                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28007                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28008
28009                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28010                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28011                 
28012                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28013                 
28014                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28015                 
28016                 break;
28017             case 180 :
28018                 
28019                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28020                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28021                 
28022                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28023                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28024                 
28025                 var targetWidth = this.minWidth - 2 * x;
28026                 var targetHeight = this.minHeight - 2 * y;
28027                 
28028                 var scale = 1;
28029                 
28030                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28031                     scale = targetWidth / width;
28032                 }
28033                 
28034                 if(x > 0 && y == 0){
28035                     scale = targetHeight / height;
28036                 }
28037                 
28038                 if(x > 0 && y > 0){
28039                     scale = targetWidth / width;
28040                     
28041                     if(width < height){
28042                         scale = targetHeight / height;
28043                     }
28044                 }
28045                 
28046                 context.scale(scale, scale);
28047                 
28048                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28049                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28050
28051                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28052                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28053
28054                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28055                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28056                 
28057                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28058                 
28059                 break;
28060             case 270 :
28061                 
28062                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28063                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28064                 
28065                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28066                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28067                 
28068                 var targetWidth = this.minWidth - 2 * x;
28069                 var targetHeight = this.minHeight - 2 * y;
28070                 
28071                 var scale = 1;
28072                 
28073                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28074                     scale = targetWidth / width;
28075                 }
28076                 
28077                 if(x > 0 && y == 0){
28078                     scale = targetHeight / height;
28079                 }
28080                 
28081                 if(x > 0 && y > 0){
28082                     scale = targetWidth / width;
28083                     
28084                     if(width < height){
28085                         scale = targetHeight / height;
28086                     }
28087                 }
28088                 
28089                 context.scale(scale, scale);
28090                 
28091                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28092                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28093
28094                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28095                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28096                 
28097                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28098                 
28099                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28100                 
28101                 break;
28102             default : 
28103                 break;
28104         }
28105         
28106         this.cropData = canvas.toDataURL(this.cropType);
28107         
28108         if(this.fireEvent('crop', this, this.cropData) !== false){
28109             this.process(this.file, this.cropData);
28110         }
28111         
28112         return;
28113         
28114     },
28115     
28116     setThumbBoxSize : function()
28117     {
28118         var width, height;
28119         
28120         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28121             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28122             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28123             
28124             this.minWidth = width;
28125             this.minHeight = height;
28126             
28127             if(this.rotate == 90 || this.rotate == 270){
28128                 this.minWidth = height;
28129                 this.minHeight = width;
28130             }
28131         }
28132         
28133         height = 300;
28134         width = Math.ceil(this.minWidth * height / this.minHeight);
28135         
28136         if(this.minWidth > this.minHeight){
28137             width = 300;
28138             height = Math.ceil(this.minHeight * width / this.minWidth);
28139         }
28140         
28141         this.thumbEl.setStyle({
28142             width : width + 'px',
28143             height : height + 'px'
28144         });
28145
28146         return;
28147             
28148     },
28149     
28150     setThumbBoxPosition : function()
28151     {
28152         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28153         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28154         
28155         this.thumbEl.setLeft(x);
28156         this.thumbEl.setTop(y);
28157         
28158     },
28159     
28160     baseRotateLevel : function()
28161     {
28162         this.baseRotate = 1;
28163         
28164         if(
28165                 typeof(this.exif) != 'undefined' &&
28166                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28167                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28168         ){
28169             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28170         }
28171         
28172         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28173         
28174     },
28175     
28176     baseScaleLevel : function()
28177     {
28178         var width, height;
28179         
28180         if(this.isDocument){
28181             
28182             if(this.baseRotate == 6 || this.baseRotate == 8){
28183             
28184                 height = this.thumbEl.getHeight();
28185                 this.baseScale = height / this.imageEl.OriginWidth;
28186
28187                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28188                     width = this.thumbEl.getWidth();
28189                     this.baseScale = width / this.imageEl.OriginHeight;
28190                 }
28191
28192                 return;
28193             }
28194
28195             height = this.thumbEl.getHeight();
28196             this.baseScale = height / this.imageEl.OriginHeight;
28197
28198             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28199                 width = this.thumbEl.getWidth();
28200                 this.baseScale = width / this.imageEl.OriginWidth;
28201             }
28202
28203             return;
28204         }
28205         
28206         if(this.baseRotate == 6 || this.baseRotate == 8){
28207             
28208             width = this.thumbEl.getHeight();
28209             this.baseScale = width / this.imageEl.OriginHeight;
28210             
28211             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28212                 height = this.thumbEl.getWidth();
28213                 this.baseScale = height / this.imageEl.OriginHeight;
28214             }
28215             
28216             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28217                 height = this.thumbEl.getWidth();
28218                 this.baseScale = height / this.imageEl.OriginHeight;
28219                 
28220                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28221                     width = this.thumbEl.getHeight();
28222                     this.baseScale = width / this.imageEl.OriginWidth;
28223                 }
28224             }
28225             
28226             return;
28227         }
28228         
28229         width = this.thumbEl.getWidth();
28230         this.baseScale = width / this.imageEl.OriginWidth;
28231         
28232         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28233             height = this.thumbEl.getHeight();
28234             this.baseScale = height / this.imageEl.OriginHeight;
28235         }
28236         
28237         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28238             
28239             height = this.thumbEl.getHeight();
28240             this.baseScale = height / this.imageEl.OriginHeight;
28241             
28242             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28243                 width = this.thumbEl.getWidth();
28244                 this.baseScale = width / this.imageEl.OriginWidth;
28245             }
28246             
28247         }
28248         
28249         return;
28250     },
28251     
28252     getScaleLevel : function()
28253     {
28254         return this.baseScale * Math.pow(1.1, this.scale);
28255     },
28256     
28257     onTouchStart : function(e)
28258     {
28259         if(!this.canvasLoaded){
28260             this.beforeSelectFile(e);
28261             return;
28262         }
28263         
28264         var touches = e.browserEvent.touches;
28265         
28266         if(!touches){
28267             return;
28268         }
28269         
28270         if(touches.length == 1){
28271             this.onMouseDown(e);
28272             return;
28273         }
28274         
28275         if(touches.length != 2){
28276             return;
28277         }
28278         
28279         var coords = [];
28280         
28281         for(var i = 0, finger; finger = touches[i]; i++){
28282             coords.push(finger.pageX, finger.pageY);
28283         }
28284         
28285         var x = Math.pow(coords[0] - coords[2], 2);
28286         var y = Math.pow(coords[1] - coords[3], 2);
28287         
28288         this.startDistance = Math.sqrt(x + y);
28289         
28290         this.startScale = this.scale;
28291         
28292         this.pinching = true;
28293         this.dragable = false;
28294         
28295     },
28296     
28297     onTouchMove : function(e)
28298     {
28299         if(!this.pinching && !this.dragable){
28300             return;
28301         }
28302         
28303         var touches = e.browserEvent.touches;
28304         
28305         if(!touches){
28306             return;
28307         }
28308         
28309         if(this.dragable){
28310             this.onMouseMove(e);
28311             return;
28312         }
28313         
28314         var coords = [];
28315         
28316         for(var i = 0, finger; finger = touches[i]; i++){
28317             coords.push(finger.pageX, finger.pageY);
28318         }
28319         
28320         var x = Math.pow(coords[0] - coords[2], 2);
28321         var y = Math.pow(coords[1] - coords[3], 2);
28322         
28323         this.endDistance = Math.sqrt(x + y);
28324         
28325         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28326         
28327         if(!this.zoomable()){
28328             this.scale = this.startScale;
28329             return;
28330         }
28331         
28332         this.draw();
28333         
28334     },
28335     
28336     onTouchEnd : function(e)
28337     {
28338         this.pinching = false;
28339         this.dragable = false;
28340         
28341     },
28342     
28343     process : function(file, crop)
28344     {
28345         if(this.loadMask){
28346             this.maskEl.mask(this.loadingText);
28347         }
28348         
28349         this.xhr = new XMLHttpRequest();
28350         
28351         file.xhr = this.xhr;
28352
28353         this.xhr.open(this.method, this.url, true);
28354         
28355         var headers = {
28356             "Accept": "application/json",
28357             "Cache-Control": "no-cache",
28358             "X-Requested-With": "XMLHttpRequest"
28359         };
28360         
28361         for (var headerName in headers) {
28362             var headerValue = headers[headerName];
28363             if (headerValue) {
28364                 this.xhr.setRequestHeader(headerName, headerValue);
28365             }
28366         }
28367         
28368         var _this = this;
28369         
28370         this.xhr.onload = function()
28371         {
28372             _this.xhrOnLoad(_this.xhr);
28373         }
28374         
28375         this.xhr.onerror = function()
28376         {
28377             _this.xhrOnError(_this.xhr);
28378         }
28379         
28380         var formData = new FormData();
28381
28382         formData.append('returnHTML', 'NO');
28383         
28384         if(crop){
28385             formData.append('crop', crop);
28386         }
28387         
28388         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28389             formData.append(this.paramName, file, file.name);
28390         }
28391         
28392         if(typeof(file.filename) != 'undefined'){
28393             formData.append('filename', file.filename);
28394         }
28395         
28396         if(typeof(file.mimetype) != 'undefined'){
28397             formData.append('mimetype', file.mimetype);
28398         }
28399         
28400         if(this.fireEvent('arrange', this, formData) != false){
28401             this.xhr.send(formData);
28402         };
28403     },
28404     
28405     xhrOnLoad : function(xhr)
28406     {
28407         if(this.loadMask){
28408             this.maskEl.unmask();
28409         }
28410         
28411         if (xhr.readyState !== 4) {
28412             this.fireEvent('exception', this, xhr);
28413             return;
28414         }
28415
28416         var response = Roo.decode(xhr.responseText);
28417         
28418         if(!response.success){
28419             this.fireEvent('exception', this, xhr);
28420             return;
28421         }
28422         
28423         var response = Roo.decode(xhr.responseText);
28424         
28425         this.fireEvent('upload', this, response);
28426         
28427     },
28428     
28429     xhrOnError : function()
28430     {
28431         if(this.loadMask){
28432             this.maskEl.unmask();
28433         }
28434         
28435         Roo.log('xhr on error');
28436         
28437         var response = Roo.decode(xhr.responseText);
28438           
28439         Roo.log(response);
28440         
28441     },
28442     
28443     prepare : function(file)
28444     {   
28445         if(this.loadMask){
28446             this.maskEl.mask(this.loadingText);
28447         }
28448         
28449         this.file = false;
28450         this.exif = {};
28451         
28452         if(typeof(file) === 'string'){
28453             this.loadCanvas(file);
28454             return;
28455         }
28456         
28457         if(!file || !this.urlAPI){
28458             return;
28459         }
28460         
28461         this.file = file;
28462         this.cropType = file.type;
28463         
28464         var _this = this;
28465         
28466         if(this.fireEvent('prepare', this, this.file) != false){
28467             
28468             var reader = new FileReader();
28469             
28470             reader.onload = function (e) {
28471                 if (e.target.error) {
28472                     Roo.log(e.target.error);
28473                     return;
28474                 }
28475                 
28476                 var buffer = e.target.result,
28477                     dataView = new DataView(buffer),
28478                     offset = 2,
28479                     maxOffset = dataView.byteLength - 4,
28480                     markerBytes,
28481                     markerLength;
28482                 
28483                 if (dataView.getUint16(0) === 0xffd8) {
28484                     while (offset < maxOffset) {
28485                         markerBytes = dataView.getUint16(offset);
28486                         
28487                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28488                             markerLength = dataView.getUint16(offset + 2) + 2;
28489                             if (offset + markerLength > dataView.byteLength) {
28490                                 Roo.log('Invalid meta data: Invalid segment size.');
28491                                 break;
28492                             }
28493                             
28494                             if(markerBytes == 0xffe1){
28495                                 _this.parseExifData(
28496                                     dataView,
28497                                     offset,
28498                                     markerLength
28499                                 );
28500                             }
28501                             
28502                             offset += markerLength;
28503                             
28504                             continue;
28505                         }
28506                         
28507                         break;
28508                     }
28509                     
28510                 }
28511                 
28512                 var url = _this.urlAPI.createObjectURL(_this.file);
28513                 
28514                 _this.loadCanvas(url);
28515                 
28516                 return;
28517             }
28518             
28519             reader.readAsArrayBuffer(this.file);
28520             
28521         }
28522         
28523     },
28524     
28525     parseExifData : function(dataView, offset, length)
28526     {
28527         var tiffOffset = offset + 10,
28528             littleEndian,
28529             dirOffset;
28530     
28531         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28532             // No Exif data, might be XMP data instead
28533             return;
28534         }
28535         
28536         // Check for the ASCII code for "Exif" (0x45786966):
28537         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28538             // No Exif data, might be XMP data instead
28539             return;
28540         }
28541         if (tiffOffset + 8 > dataView.byteLength) {
28542             Roo.log('Invalid Exif data: Invalid segment size.');
28543             return;
28544         }
28545         // Check for the two null bytes:
28546         if (dataView.getUint16(offset + 8) !== 0x0000) {
28547             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28548             return;
28549         }
28550         // Check the byte alignment:
28551         switch (dataView.getUint16(tiffOffset)) {
28552         case 0x4949:
28553             littleEndian = true;
28554             break;
28555         case 0x4D4D:
28556             littleEndian = false;
28557             break;
28558         default:
28559             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28560             return;
28561         }
28562         // Check for the TIFF tag marker (0x002A):
28563         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28564             Roo.log('Invalid Exif data: Missing TIFF marker.');
28565             return;
28566         }
28567         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28568         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28569         
28570         this.parseExifTags(
28571             dataView,
28572             tiffOffset,
28573             tiffOffset + dirOffset,
28574             littleEndian
28575         );
28576     },
28577     
28578     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28579     {
28580         var tagsNumber,
28581             dirEndOffset,
28582             i;
28583         if (dirOffset + 6 > dataView.byteLength) {
28584             Roo.log('Invalid Exif data: Invalid directory offset.');
28585             return;
28586         }
28587         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28588         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28589         if (dirEndOffset + 4 > dataView.byteLength) {
28590             Roo.log('Invalid Exif data: Invalid directory size.');
28591             return;
28592         }
28593         for (i = 0; i < tagsNumber; i += 1) {
28594             this.parseExifTag(
28595                 dataView,
28596                 tiffOffset,
28597                 dirOffset + 2 + 12 * i, // tag offset
28598                 littleEndian
28599             );
28600         }
28601         // Return the offset to the next directory:
28602         return dataView.getUint32(dirEndOffset, littleEndian);
28603     },
28604     
28605     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28606     {
28607         var tag = dataView.getUint16(offset, littleEndian);
28608         
28609         this.exif[tag] = this.getExifValue(
28610             dataView,
28611             tiffOffset,
28612             offset,
28613             dataView.getUint16(offset + 2, littleEndian), // tag type
28614             dataView.getUint32(offset + 4, littleEndian), // tag length
28615             littleEndian
28616         );
28617     },
28618     
28619     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28620     {
28621         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28622             tagSize,
28623             dataOffset,
28624             values,
28625             i,
28626             str,
28627             c;
28628     
28629         if (!tagType) {
28630             Roo.log('Invalid Exif data: Invalid tag type.');
28631             return;
28632         }
28633         
28634         tagSize = tagType.size * length;
28635         // Determine if the value is contained in the dataOffset bytes,
28636         // or if the value at the dataOffset is a pointer to the actual data:
28637         dataOffset = tagSize > 4 ?
28638                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28639         if (dataOffset + tagSize > dataView.byteLength) {
28640             Roo.log('Invalid Exif data: Invalid data offset.');
28641             return;
28642         }
28643         if (length === 1) {
28644             return tagType.getValue(dataView, dataOffset, littleEndian);
28645         }
28646         values = [];
28647         for (i = 0; i < length; i += 1) {
28648             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28649         }
28650         
28651         if (tagType.ascii) {
28652             str = '';
28653             // Concatenate the chars:
28654             for (i = 0; i < values.length; i += 1) {
28655                 c = values[i];
28656                 // Ignore the terminating NULL byte(s):
28657                 if (c === '\u0000') {
28658                     break;
28659                 }
28660                 str += c;
28661             }
28662             return str;
28663         }
28664         return values;
28665     }
28666     
28667 });
28668
28669 Roo.apply(Roo.bootstrap.UploadCropbox, {
28670     tags : {
28671         'Orientation': 0x0112
28672     },
28673     
28674     Orientation: {
28675             1: 0, //'top-left',
28676 //            2: 'top-right',
28677             3: 180, //'bottom-right',
28678 //            4: 'bottom-left',
28679 //            5: 'left-top',
28680             6: 90, //'right-top',
28681 //            7: 'right-bottom',
28682             8: 270 //'left-bottom'
28683     },
28684     
28685     exifTagTypes : {
28686         // byte, 8-bit unsigned int:
28687         1: {
28688             getValue: function (dataView, dataOffset) {
28689                 return dataView.getUint8(dataOffset);
28690             },
28691             size: 1
28692         },
28693         // ascii, 8-bit byte:
28694         2: {
28695             getValue: function (dataView, dataOffset) {
28696                 return String.fromCharCode(dataView.getUint8(dataOffset));
28697             },
28698             size: 1,
28699             ascii: true
28700         },
28701         // short, 16 bit int:
28702         3: {
28703             getValue: function (dataView, dataOffset, littleEndian) {
28704                 return dataView.getUint16(dataOffset, littleEndian);
28705             },
28706             size: 2
28707         },
28708         // long, 32 bit int:
28709         4: {
28710             getValue: function (dataView, dataOffset, littleEndian) {
28711                 return dataView.getUint32(dataOffset, littleEndian);
28712             },
28713             size: 4
28714         },
28715         // rational = two long values, first is numerator, second is denominator:
28716         5: {
28717             getValue: function (dataView, dataOffset, littleEndian) {
28718                 return dataView.getUint32(dataOffset, littleEndian) /
28719                     dataView.getUint32(dataOffset + 4, littleEndian);
28720             },
28721             size: 8
28722         },
28723         // slong, 32 bit signed int:
28724         9: {
28725             getValue: function (dataView, dataOffset, littleEndian) {
28726                 return dataView.getInt32(dataOffset, littleEndian);
28727             },
28728             size: 4
28729         },
28730         // srational, two slongs, first is numerator, second is denominator:
28731         10: {
28732             getValue: function (dataView, dataOffset, littleEndian) {
28733                 return dataView.getInt32(dataOffset, littleEndian) /
28734                     dataView.getInt32(dataOffset + 4, littleEndian);
28735             },
28736             size: 8
28737         }
28738     },
28739     
28740     footer : {
28741         STANDARD : [
28742             {
28743                 tag : 'div',
28744                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28745                 action : 'rotate-left',
28746                 cn : [
28747                     {
28748                         tag : 'button',
28749                         cls : 'btn btn-default',
28750                         html : '<i class="fa fa-undo"></i>'
28751                     }
28752                 ]
28753             },
28754             {
28755                 tag : 'div',
28756                 cls : 'btn-group roo-upload-cropbox-picture',
28757                 action : 'picture',
28758                 cn : [
28759                     {
28760                         tag : 'button',
28761                         cls : 'btn btn-default',
28762                         html : '<i class="fa fa-picture-o"></i>'
28763                     }
28764                 ]
28765             },
28766             {
28767                 tag : 'div',
28768                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28769                 action : 'rotate-right',
28770                 cn : [
28771                     {
28772                         tag : 'button',
28773                         cls : 'btn btn-default',
28774                         html : '<i class="fa fa-repeat"></i>'
28775                     }
28776                 ]
28777             }
28778         ],
28779         DOCUMENT : [
28780             {
28781                 tag : 'div',
28782                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28783                 action : 'rotate-left',
28784                 cn : [
28785                     {
28786                         tag : 'button',
28787                         cls : 'btn btn-default',
28788                         html : '<i class="fa fa-undo"></i>'
28789                     }
28790                 ]
28791             },
28792             {
28793                 tag : 'div',
28794                 cls : 'btn-group roo-upload-cropbox-download',
28795                 action : 'download',
28796                 cn : [
28797                     {
28798                         tag : 'button',
28799                         cls : 'btn btn-default',
28800                         html : '<i class="fa fa-download"></i>'
28801                     }
28802                 ]
28803             },
28804             {
28805                 tag : 'div',
28806                 cls : 'btn-group roo-upload-cropbox-crop',
28807                 action : 'crop',
28808                 cn : [
28809                     {
28810                         tag : 'button',
28811                         cls : 'btn btn-default',
28812                         html : '<i class="fa fa-crop"></i>'
28813                     }
28814                 ]
28815             },
28816             {
28817                 tag : 'div',
28818                 cls : 'btn-group roo-upload-cropbox-trash',
28819                 action : 'trash',
28820                 cn : [
28821                     {
28822                         tag : 'button',
28823                         cls : 'btn btn-default',
28824                         html : '<i class="fa fa-trash"></i>'
28825                     }
28826                 ]
28827             },
28828             {
28829                 tag : 'div',
28830                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28831                 action : 'rotate-right',
28832                 cn : [
28833                     {
28834                         tag : 'button',
28835                         cls : 'btn btn-default',
28836                         html : '<i class="fa fa-repeat"></i>'
28837                     }
28838                 ]
28839             }
28840         ],
28841         ROTATOR : [
28842             {
28843                 tag : 'div',
28844                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28845                 action : 'rotate-left',
28846                 cn : [
28847                     {
28848                         tag : 'button',
28849                         cls : 'btn btn-default',
28850                         html : '<i class="fa fa-undo"></i>'
28851                     }
28852                 ]
28853             },
28854             {
28855                 tag : 'div',
28856                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28857                 action : 'rotate-right',
28858                 cn : [
28859                     {
28860                         tag : 'button',
28861                         cls : 'btn btn-default',
28862                         html : '<i class="fa fa-repeat"></i>'
28863                     }
28864                 ]
28865             }
28866         ]
28867     }
28868 });
28869
28870 /*
28871 * Licence: LGPL
28872 */
28873
28874 /**
28875  * @class Roo.bootstrap.DocumentManager
28876  * @extends Roo.bootstrap.Component
28877  * Bootstrap DocumentManager class
28878  * @cfg {String} paramName default 'imageUpload'
28879  * @cfg {String} toolTipName default 'filename'
28880  * @cfg {String} method default POST
28881  * @cfg {String} url action url
28882  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28883  * @cfg {Boolean} multiple multiple upload default true
28884  * @cfg {Number} thumbSize default 300
28885  * @cfg {String} fieldLabel
28886  * @cfg {Number} labelWidth default 4
28887  * @cfg {String} labelAlign (left|top) default left
28888  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28889 * @cfg {Number} labellg set the width of label (1-12)
28890  * @cfg {Number} labelmd set the width of label (1-12)
28891  * @cfg {Number} labelsm set the width of label (1-12)
28892  * @cfg {Number} labelxs set the width of label (1-12)
28893  * 
28894  * @constructor
28895  * Create a new DocumentManager
28896  * @param {Object} config The config object
28897  */
28898
28899 Roo.bootstrap.DocumentManager = function(config){
28900     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28901     
28902     this.files = [];
28903     this.delegates = [];
28904     
28905     this.addEvents({
28906         /**
28907          * @event initial
28908          * Fire when initial the DocumentManager
28909          * @param {Roo.bootstrap.DocumentManager} this
28910          */
28911         "initial" : true,
28912         /**
28913          * @event inspect
28914          * inspect selected file
28915          * @param {Roo.bootstrap.DocumentManager} this
28916          * @param {File} file
28917          */
28918         "inspect" : true,
28919         /**
28920          * @event exception
28921          * Fire when xhr load exception
28922          * @param {Roo.bootstrap.DocumentManager} this
28923          * @param {XMLHttpRequest} xhr
28924          */
28925         "exception" : true,
28926         /**
28927          * @event afterupload
28928          * Fire when xhr load exception
28929          * @param {Roo.bootstrap.DocumentManager} this
28930          * @param {XMLHttpRequest} xhr
28931          */
28932         "afterupload" : true,
28933         /**
28934          * @event prepare
28935          * prepare the form data
28936          * @param {Roo.bootstrap.DocumentManager} this
28937          * @param {Object} formData
28938          */
28939         "prepare" : true,
28940         /**
28941          * @event remove
28942          * Fire when remove the file
28943          * @param {Roo.bootstrap.DocumentManager} this
28944          * @param {Object} file
28945          */
28946         "remove" : true,
28947         /**
28948          * @event refresh
28949          * Fire after refresh the file
28950          * @param {Roo.bootstrap.DocumentManager} this
28951          */
28952         "refresh" : true,
28953         /**
28954          * @event click
28955          * Fire after click the image
28956          * @param {Roo.bootstrap.DocumentManager} this
28957          * @param {Object} file
28958          */
28959         "click" : true,
28960         /**
28961          * @event edit
28962          * Fire when upload a image and editable set to true
28963          * @param {Roo.bootstrap.DocumentManager} this
28964          * @param {Object} file
28965          */
28966         "edit" : true,
28967         /**
28968          * @event beforeselectfile
28969          * Fire before select file
28970          * @param {Roo.bootstrap.DocumentManager} this
28971          */
28972         "beforeselectfile" : true,
28973         /**
28974          * @event process
28975          * Fire before process file
28976          * @param {Roo.bootstrap.DocumentManager} this
28977          * @param {Object} file
28978          */
28979         "process" : true,
28980         /**
28981          * @event previewrendered
28982          * Fire when preview rendered
28983          * @param {Roo.bootstrap.DocumentManager} this
28984          * @param {Object} file
28985          */
28986         "previewrendered" : true,
28987         /**
28988          */
28989         "previewResize" : true
28990         
28991     });
28992 };
28993
28994 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28995     
28996     boxes : 0,
28997     inputName : '',
28998     thumbSize : 300,
28999     multiple : true,
29000     files : false,
29001     method : 'POST',
29002     url : '',
29003     paramName : 'imageUpload',
29004     toolTipName : 'filename',
29005     fieldLabel : '',
29006     labelWidth : 4,
29007     labelAlign : 'left',
29008     editable : true,
29009     delegates : false,
29010     xhr : false, 
29011     
29012     labellg : 0,
29013     labelmd : 0,
29014     labelsm : 0,
29015     labelxs : 0,
29016     
29017     getAutoCreate : function()
29018     {   
29019         var managerWidget = {
29020             tag : 'div',
29021             cls : 'roo-document-manager',
29022             cn : [
29023                 {
29024                     tag : 'input',
29025                     cls : 'roo-document-manager-selector',
29026                     type : 'file'
29027                 },
29028                 {
29029                     tag : 'div',
29030                     cls : 'roo-document-manager-uploader',
29031                     cn : [
29032                         {
29033                             tag : 'div',
29034                             cls : 'roo-document-manager-upload-btn',
29035                             html : '<i class="fa fa-plus"></i>'
29036                         }
29037                     ]
29038                     
29039                 }
29040             ]
29041         };
29042         
29043         var content = [
29044             {
29045                 tag : 'div',
29046                 cls : 'column col-md-12',
29047                 cn : managerWidget
29048             }
29049         ];
29050         
29051         if(this.fieldLabel.length){
29052             
29053             content = [
29054                 {
29055                     tag : 'div',
29056                     cls : 'column col-md-12',
29057                     html : this.fieldLabel
29058                 },
29059                 {
29060                     tag : 'div',
29061                     cls : 'column col-md-12',
29062                     cn : managerWidget
29063                 }
29064             ];
29065
29066             if(this.labelAlign == 'left'){
29067                 content = [
29068                     {
29069                         tag : 'div',
29070                         cls : 'column',
29071                         html : this.fieldLabel
29072                     },
29073                     {
29074                         tag : 'div',
29075                         cls : 'column',
29076                         cn : managerWidget
29077                     }
29078                 ];
29079                 
29080                 if(this.labelWidth > 12){
29081                     content[0].style = "width: " + this.labelWidth + 'px';
29082                 }
29083
29084                 if(this.labelWidth < 13 && this.labelmd == 0){
29085                     this.labelmd = this.labelWidth;
29086                 }
29087
29088                 if(this.labellg > 0){
29089                     content[0].cls += ' col-lg-' + this.labellg;
29090                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29091                 }
29092
29093                 if(this.labelmd > 0){
29094                     content[0].cls += ' col-md-' + this.labelmd;
29095                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29096                 }
29097
29098                 if(this.labelsm > 0){
29099                     content[0].cls += ' col-sm-' + this.labelsm;
29100                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29101                 }
29102
29103                 if(this.labelxs > 0){
29104                     content[0].cls += ' col-xs-' + this.labelxs;
29105                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29106                 }
29107                 
29108             }
29109         }
29110         
29111         var cfg = {
29112             tag : 'div',
29113             cls : 'row clearfix',
29114             cn : content
29115         };
29116         
29117         return cfg;
29118         
29119     },
29120     
29121     initEvents : function()
29122     {
29123         this.managerEl = this.el.select('.roo-document-manager', true).first();
29124         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29125         
29126         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29127         this.selectorEl.hide();
29128         
29129         if(this.multiple){
29130             this.selectorEl.attr('multiple', 'multiple');
29131         }
29132         
29133         this.selectorEl.on('change', this.onFileSelected, this);
29134         
29135         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29136         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29137         
29138         this.uploader.on('click', this.onUploaderClick, this);
29139         
29140         this.renderProgressDialog();
29141         
29142         var _this = this;
29143         
29144         window.addEventListener("resize", function() { _this.refresh(); } );
29145         
29146         this.fireEvent('initial', this);
29147     },
29148     
29149     renderProgressDialog : function()
29150     {
29151         var _this = this;
29152         
29153         this.progressDialog = new Roo.bootstrap.Modal({
29154             cls : 'roo-document-manager-progress-dialog',
29155             allow_close : false,
29156             title : '',
29157             buttons : [
29158                 {
29159                     name  :'cancel',
29160                     weight : 'danger',
29161                     html : 'Cancel'
29162                 }
29163             ], 
29164             listeners : { 
29165                 btnclick : function() {
29166                     _this.uploadCancel();
29167                     this.hide();
29168                 }
29169             }
29170         });
29171          
29172         this.progressDialog.render(Roo.get(document.body));
29173          
29174         this.progress = new Roo.bootstrap.Progress({
29175             cls : 'roo-document-manager-progress',
29176             active : true,
29177             striped : true
29178         });
29179         
29180         this.progress.render(this.progressDialog.getChildContainer());
29181         
29182         this.progressBar = new Roo.bootstrap.ProgressBar({
29183             cls : 'roo-document-manager-progress-bar',
29184             aria_valuenow : 0,
29185             aria_valuemin : 0,
29186             aria_valuemax : 12,
29187             panel : 'success'
29188         });
29189         
29190         this.progressBar.render(this.progress.getChildContainer());
29191     },
29192     
29193     onUploaderClick : function(e)
29194     {
29195         e.preventDefault();
29196      
29197         if(this.fireEvent('beforeselectfile', this) != false){
29198             this.selectorEl.dom.click();
29199         }
29200         
29201     },
29202     
29203     onFileSelected : function(e)
29204     {
29205         e.preventDefault();
29206         
29207         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29208             return;
29209         }
29210         
29211         Roo.each(this.selectorEl.dom.files, function(file){
29212             if(this.fireEvent('inspect', this, file) != false){
29213                 this.files.push(file);
29214             }
29215         }, this);
29216         
29217         this.queue();
29218         
29219     },
29220     
29221     queue : function()
29222     {
29223         this.selectorEl.dom.value = '';
29224         
29225         if(!this.files || !this.files.length){
29226             return;
29227         }
29228         
29229         if(this.boxes > 0 && this.files.length > this.boxes){
29230             this.files = this.files.slice(0, this.boxes);
29231         }
29232         
29233         this.uploader.show();
29234         
29235         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29236             this.uploader.hide();
29237         }
29238         
29239         var _this = this;
29240         
29241         var files = [];
29242         
29243         var docs = [];
29244         
29245         Roo.each(this.files, function(file){
29246             
29247             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29248                 var f = this.renderPreview(file);
29249                 files.push(f);
29250                 return;
29251             }
29252             
29253             if(file.type.indexOf('image') != -1){
29254                 this.delegates.push(
29255                     (function(){
29256                         _this.process(file);
29257                     }).createDelegate(this)
29258                 );
29259         
29260                 return;
29261             }
29262             
29263             docs.push(
29264                 (function(){
29265                     _this.process(file);
29266                 }).createDelegate(this)
29267             );
29268             
29269         }, this);
29270         
29271         this.files = files;
29272         
29273         this.delegates = this.delegates.concat(docs);
29274         
29275         if(!this.delegates.length){
29276             this.refresh();
29277             return;
29278         }
29279         
29280         this.progressBar.aria_valuemax = this.delegates.length;
29281         
29282         this.arrange();
29283         
29284         return;
29285     },
29286     
29287     arrange : function()
29288     {
29289         if(!this.delegates.length){
29290             this.progressDialog.hide();
29291             this.refresh();
29292             return;
29293         }
29294         
29295         var delegate = this.delegates.shift();
29296         
29297         this.progressDialog.show();
29298         
29299         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29300         
29301         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29302         
29303         delegate();
29304     },
29305     
29306     refresh : function()
29307     {
29308         this.uploader.show();
29309         
29310         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29311             this.uploader.hide();
29312         }
29313         
29314         Roo.isTouch ? this.closable(false) : this.closable(true);
29315         
29316         this.fireEvent('refresh', this);
29317     },
29318     
29319     onRemove : function(e, el, o)
29320     {
29321         e.preventDefault();
29322         
29323         this.fireEvent('remove', this, o);
29324         
29325     },
29326     
29327     remove : function(o)
29328     {
29329         var files = [];
29330         
29331         Roo.each(this.files, function(file){
29332             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29333                 files.push(file);
29334                 return;
29335             }
29336
29337             o.target.remove();
29338
29339         }, this);
29340         
29341         this.files = files;
29342         
29343         this.refresh();
29344     },
29345     
29346     clear : function()
29347     {
29348         Roo.each(this.files, function(file){
29349             if(!file.target){
29350                 return;
29351             }
29352             
29353             file.target.remove();
29354
29355         }, this);
29356         
29357         this.files = [];
29358         
29359         this.refresh();
29360     },
29361     
29362     onClick : function(e, el, o)
29363     {
29364         e.preventDefault();
29365         
29366         this.fireEvent('click', this, o);
29367         
29368     },
29369     
29370     closable : function(closable)
29371     {
29372         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29373             
29374             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29375             
29376             if(closable){
29377                 el.show();
29378                 return;
29379             }
29380             
29381             el.hide();
29382             
29383         }, this);
29384     },
29385     
29386     xhrOnLoad : function(xhr)
29387     {
29388         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29389             el.remove();
29390         }, this);
29391         
29392         if (xhr.readyState !== 4) {
29393             this.arrange();
29394             this.fireEvent('exception', this, xhr);
29395             return;
29396         }
29397
29398         var response = Roo.decode(xhr.responseText);
29399         
29400         if(!response.success){
29401             this.arrange();
29402             this.fireEvent('exception', this, xhr);
29403             return;
29404         }
29405         
29406         var file = this.renderPreview(response.data);
29407         
29408         this.files.push(file);
29409         
29410         this.arrange();
29411         
29412         this.fireEvent('afterupload', this, xhr);
29413         
29414     },
29415     
29416     xhrOnError : function(xhr)
29417     {
29418         Roo.log('xhr on error');
29419         
29420         var response = Roo.decode(xhr.responseText);
29421           
29422         Roo.log(response);
29423         
29424         this.arrange();
29425     },
29426     
29427     process : function(file)
29428     {
29429         if(this.fireEvent('process', this, file) !== false){
29430             if(this.editable && file.type.indexOf('image') != -1){
29431                 this.fireEvent('edit', this, file);
29432                 return;
29433             }
29434
29435             this.uploadStart(file, false);
29436
29437             return;
29438         }
29439         
29440     },
29441     
29442     uploadStart : function(file, crop)
29443     {
29444         this.xhr = new XMLHttpRequest();
29445         
29446         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29447             this.arrange();
29448             return;
29449         }
29450         
29451         file.xhr = this.xhr;
29452             
29453         this.managerEl.createChild({
29454             tag : 'div',
29455             cls : 'roo-document-manager-loading',
29456             cn : [
29457                 {
29458                     tag : 'div',
29459                     tooltip : file.name,
29460                     cls : 'roo-document-manager-thumb',
29461                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29462                 }
29463             ]
29464
29465         });
29466
29467         this.xhr.open(this.method, this.url, true);
29468         
29469         var headers = {
29470             "Accept": "application/json",
29471             "Cache-Control": "no-cache",
29472             "X-Requested-With": "XMLHttpRequest"
29473         };
29474         
29475         for (var headerName in headers) {
29476             var headerValue = headers[headerName];
29477             if (headerValue) {
29478                 this.xhr.setRequestHeader(headerName, headerValue);
29479             }
29480         }
29481         
29482         var _this = this;
29483         
29484         this.xhr.onload = function()
29485         {
29486             _this.xhrOnLoad(_this.xhr);
29487         }
29488         
29489         this.xhr.onerror = function()
29490         {
29491             _this.xhrOnError(_this.xhr);
29492         }
29493         
29494         var formData = new FormData();
29495
29496         formData.append('returnHTML', 'NO');
29497         
29498         if(crop){
29499             formData.append('crop', crop);
29500         }
29501         
29502         formData.append(this.paramName, file, file.name);
29503         
29504         var options = {
29505             file : file, 
29506             manually : false
29507         };
29508         
29509         if(this.fireEvent('prepare', this, formData, options) != false){
29510             
29511             if(options.manually){
29512                 return;
29513             }
29514             
29515             this.xhr.send(formData);
29516             return;
29517         };
29518         
29519         this.uploadCancel();
29520     },
29521     
29522     uploadCancel : function()
29523     {
29524         if (this.xhr) {
29525             this.xhr.abort();
29526         }
29527         
29528         this.delegates = [];
29529         
29530         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29531             el.remove();
29532         }, this);
29533         
29534         this.arrange();
29535     },
29536     
29537     renderPreview : function(file)
29538     {
29539         if(typeof(file.target) != 'undefined' && file.target){
29540             return file;
29541         }
29542         
29543         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29544         
29545         var previewEl = this.managerEl.createChild({
29546             tag : 'div',
29547             cls : 'roo-document-manager-preview',
29548             cn : [
29549                 {
29550                     tag : 'div',
29551                     tooltip : file[this.toolTipName],
29552                     cls : 'roo-document-manager-thumb',
29553                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29554                 },
29555                 {
29556                     tag : 'button',
29557                     cls : 'close',
29558                     html : '<i class="fa fa-times-circle"></i>'
29559                 }
29560             ]
29561         });
29562
29563         var close = previewEl.select('button.close', true).first();
29564
29565         close.on('click', this.onRemove, this, file);
29566
29567         file.target = previewEl;
29568
29569         var image = previewEl.select('img', true).first();
29570         
29571         var _this = this;
29572         
29573         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29574         
29575         image.on('click', this.onClick, this, file);
29576         
29577         this.fireEvent('previewrendered', this, file);
29578         
29579         return file;
29580         
29581     },
29582     
29583     onPreviewLoad : function(file, image)
29584     {
29585         if(typeof(file.target) == 'undefined' || !file.target){
29586             return;
29587         }
29588         
29589         var width = image.dom.naturalWidth || image.dom.width;
29590         var height = image.dom.naturalHeight || image.dom.height;
29591         
29592         if(!this.previewResize) {
29593             return;
29594         }
29595         
29596         if(width > height){
29597             file.target.addClass('wide');
29598             return;
29599         }
29600         
29601         file.target.addClass('tall');
29602         return;
29603         
29604     },
29605     
29606     uploadFromSource : function(file, crop)
29607     {
29608         this.xhr = new XMLHttpRequest();
29609         
29610         this.managerEl.createChild({
29611             tag : 'div',
29612             cls : 'roo-document-manager-loading',
29613             cn : [
29614                 {
29615                     tag : 'div',
29616                     tooltip : file.name,
29617                     cls : 'roo-document-manager-thumb',
29618                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29619                 }
29620             ]
29621
29622         });
29623
29624         this.xhr.open(this.method, this.url, true);
29625         
29626         var headers = {
29627             "Accept": "application/json",
29628             "Cache-Control": "no-cache",
29629             "X-Requested-With": "XMLHttpRequest"
29630         };
29631         
29632         for (var headerName in headers) {
29633             var headerValue = headers[headerName];
29634             if (headerValue) {
29635                 this.xhr.setRequestHeader(headerName, headerValue);
29636             }
29637         }
29638         
29639         var _this = this;
29640         
29641         this.xhr.onload = function()
29642         {
29643             _this.xhrOnLoad(_this.xhr);
29644         }
29645         
29646         this.xhr.onerror = function()
29647         {
29648             _this.xhrOnError(_this.xhr);
29649         }
29650         
29651         var formData = new FormData();
29652
29653         formData.append('returnHTML', 'NO');
29654         
29655         formData.append('crop', crop);
29656         
29657         if(typeof(file.filename) != 'undefined'){
29658             formData.append('filename', file.filename);
29659         }
29660         
29661         if(typeof(file.mimetype) != 'undefined'){
29662             formData.append('mimetype', file.mimetype);
29663         }
29664         
29665         Roo.log(formData);
29666         
29667         if(this.fireEvent('prepare', this, formData) != false){
29668             this.xhr.send(formData);
29669         };
29670     }
29671 });
29672
29673 /*
29674 * Licence: LGPL
29675 */
29676
29677 /**
29678  * @class Roo.bootstrap.DocumentViewer
29679  * @extends Roo.bootstrap.Component
29680  * Bootstrap DocumentViewer class
29681  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29682  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29683  * 
29684  * @constructor
29685  * Create a new DocumentViewer
29686  * @param {Object} config The config object
29687  */
29688
29689 Roo.bootstrap.DocumentViewer = function(config){
29690     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29691     
29692     this.addEvents({
29693         /**
29694          * @event initial
29695          * Fire after initEvent
29696          * @param {Roo.bootstrap.DocumentViewer} this
29697          */
29698         "initial" : true,
29699         /**
29700          * @event click
29701          * Fire after click
29702          * @param {Roo.bootstrap.DocumentViewer} this
29703          */
29704         "click" : true,
29705         /**
29706          * @event download
29707          * Fire after download button
29708          * @param {Roo.bootstrap.DocumentViewer} this
29709          */
29710         "download" : true,
29711         /**
29712          * @event trash
29713          * Fire after trash button
29714          * @param {Roo.bootstrap.DocumentViewer} this
29715          */
29716         "trash" : true
29717         
29718     });
29719 };
29720
29721 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29722     
29723     showDownload : true,
29724     
29725     showTrash : true,
29726     
29727     getAutoCreate : function()
29728     {
29729         var cfg = {
29730             tag : 'div',
29731             cls : 'roo-document-viewer',
29732             cn : [
29733                 {
29734                     tag : 'div',
29735                     cls : 'roo-document-viewer-body',
29736                     cn : [
29737                         {
29738                             tag : 'div',
29739                             cls : 'roo-document-viewer-thumb',
29740                             cn : [
29741                                 {
29742                                     tag : 'img',
29743                                     cls : 'roo-document-viewer-image'
29744                                 }
29745                             ]
29746                         }
29747                     ]
29748                 },
29749                 {
29750                     tag : 'div',
29751                     cls : 'roo-document-viewer-footer',
29752                     cn : {
29753                         tag : 'div',
29754                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29755                         cn : [
29756                             {
29757                                 tag : 'div',
29758                                 cls : 'btn-group roo-document-viewer-download',
29759                                 cn : [
29760                                     {
29761                                         tag : 'button',
29762                                         cls : 'btn btn-default',
29763                                         html : '<i class="fa fa-download"></i>'
29764                                     }
29765                                 ]
29766                             },
29767                             {
29768                                 tag : 'div',
29769                                 cls : 'btn-group roo-document-viewer-trash',
29770                                 cn : [
29771                                     {
29772                                         tag : 'button',
29773                                         cls : 'btn btn-default',
29774                                         html : '<i class="fa fa-trash"></i>'
29775                                     }
29776                                 ]
29777                             }
29778                         ]
29779                     }
29780                 }
29781             ]
29782         };
29783         
29784         return cfg;
29785     },
29786     
29787     initEvents : function()
29788     {
29789         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29790         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29791         
29792         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29793         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29794         
29795         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29796         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29797         
29798         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29799         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29800         
29801         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29802         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29803         
29804         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29805         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29806         
29807         this.bodyEl.on('click', this.onClick, this);
29808         this.downloadBtn.on('click', this.onDownload, this);
29809         this.trashBtn.on('click', this.onTrash, this);
29810         
29811         this.downloadBtn.hide();
29812         this.trashBtn.hide();
29813         
29814         if(this.showDownload){
29815             this.downloadBtn.show();
29816         }
29817         
29818         if(this.showTrash){
29819             this.trashBtn.show();
29820         }
29821         
29822         if(!this.showDownload && !this.showTrash) {
29823             this.footerEl.hide();
29824         }
29825         
29826     },
29827     
29828     initial : function()
29829     {
29830         this.fireEvent('initial', this);
29831         
29832     },
29833     
29834     onClick : function(e)
29835     {
29836         e.preventDefault();
29837         
29838         this.fireEvent('click', this);
29839     },
29840     
29841     onDownload : function(e)
29842     {
29843         e.preventDefault();
29844         
29845         this.fireEvent('download', this);
29846     },
29847     
29848     onTrash : function(e)
29849     {
29850         e.preventDefault();
29851         
29852         this.fireEvent('trash', this);
29853     }
29854     
29855 });
29856 /*
29857  * - LGPL
29858  *
29859  * nav progress bar
29860  * 
29861  */
29862
29863 /**
29864  * @class Roo.bootstrap.NavProgressBar
29865  * @extends Roo.bootstrap.Component
29866  * Bootstrap NavProgressBar class
29867  * 
29868  * @constructor
29869  * Create a new nav progress bar
29870  * @param {Object} config The config object
29871  */
29872
29873 Roo.bootstrap.NavProgressBar = function(config){
29874     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29875
29876     this.bullets = this.bullets || [];
29877    
29878 //    Roo.bootstrap.NavProgressBar.register(this);
29879      this.addEvents({
29880         /**
29881              * @event changed
29882              * Fires when the active item changes
29883              * @param {Roo.bootstrap.NavProgressBar} this
29884              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29885              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29886          */
29887         'changed': true
29888      });
29889     
29890 };
29891
29892 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29893     
29894     bullets : [],
29895     barItems : [],
29896     
29897     getAutoCreate : function()
29898     {
29899         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29900         
29901         cfg = {
29902             tag : 'div',
29903             cls : 'roo-navigation-bar-group',
29904             cn : [
29905                 {
29906                     tag : 'div',
29907                     cls : 'roo-navigation-top-bar'
29908                 },
29909                 {
29910                     tag : 'div',
29911                     cls : 'roo-navigation-bullets-bar',
29912                     cn : [
29913                         {
29914                             tag : 'ul',
29915                             cls : 'roo-navigation-bar'
29916                         }
29917                     ]
29918                 },
29919                 
29920                 {
29921                     tag : 'div',
29922                     cls : 'roo-navigation-bottom-bar'
29923                 }
29924             ]
29925             
29926         };
29927         
29928         return cfg;
29929         
29930     },
29931     
29932     initEvents: function() 
29933     {
29934         
29935     },
29936     
29937     onRender : function(ct, position) 
29938     {
29939         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29940         
29941         if(this.bullets.length){
29942             Roo.each(this.bullets, function(b){
29943                this.addItem(b);
29944             }, this);
29945         }
29946         
29947         this.format();
29948         
29949     },
29950     
29951     addItem : function(cfg)
29952     {
29953         var item = new Roo.bootstrap.NavProgressItem(cfg);
29954         
29955         item.parentId = this.id;
29956         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29957         
29958         if(cfg.html){
29959             var top = new Roo.bootstrap.Element({
29960                 tag : 'div',
29961                 cls : 'roo-navigation-bar-text'
29962             });
29963             
29964             var bottom = new Roo.bootstrap.Element({
29965                 tag : 'div',
29966                 cls : 'roo-navigation-bar-text'
29967             });
29968             
29969             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29970             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29971             
29972             var topText = new Roo.bootstrap.Element({
29973                 tag : 'span',
29974                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29975             });
29976             
29977             var bottomText = new Roo.bootstrap.Element({
29978                 tag : 'span',
29979                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29980             });
29981             
29982             topText.onRender(top.el, null);
29983             bottomText.onRender(bottom.el, null);
29984             
29985             item.topEl = top;
29986             item.bottomEl = bottom;
29987         }
29988         
29989         this.barItems.push(item);
29990         
29991         return item;
29992     },
29993     
29994     getActive : function()
29995     {
29996         var active = false;
29997         
29998         Roo.each(this.barItems, function(v){
29999             
30000             if (!v.isActive()) {
30001                 return;
30002             }
30003             
30004             active = v;
30005             return false;
30006             
30007         });
30008         
30009         return active;
30010     },
30011     
30012     setActiveItem : function(item)
30013     {
30014         var prev = false;
30015         
30016         Roo.each(this.barItems, function(v){
30017             if (v.rid == item.rid) {
30018                 return ;
30019             }
30020             
30021             if (v.isActive()) {
30022                 v.setActive(false);
30023                 prev = v;
30024             }
30025         });
30026
30027         item.setActive(true);
30028         
30029         this.fireEvent('changed', this, item, prev);
30030     },
30031     
30032     getBarItem: function(rid)
30033     {
30034         var ret = false;
30035         
30036         Roo.each(this.barItems, function(e) {
30037             if (e.rid != rid) {
30038                 return;
30039             }
30040             
30041             ret =  e;
30042             return false;
30043         });
30044         
30045         return ret;
30046     },
30047     
30048     indexOfItem : function(item)
30049     {
30050         var index = false;
30051         
30052         Roo.each(this.barItems, function(v, i){
30053             
30054             if (v.rid != item.rid) {
30055                 return;
30056             }
30057             
30058             index = i;
30059             return false
30060         });
30061         
30062         return index;
30063     },
30064     
30065     setActiveNext : function()
30066     {
30067         var i = this.indexOfItem(this.getActive());
30068         
30069         if (i > this.barItems.length) {
30070             return;
30071         }
30072         
30073         this.setActiveItem(this.barItems[i+1]);
30074     },
30075     
30076     setActivePrev : function()
30077     {
30078         var i = this.indexOfItem(this.getActive());
30079         
30080         if (i  < 1) {
30081             return;
30082         }
30083         
30084         this.setActiveItem(this.barItems[i-1]);
30085     },
30086     
30087     format : function()
30088     {
30089         if(!this.barItems.length){
30090             return;
30091         }
30092      
30093         var width = 100 / this.barItems.length;
30094         
30095         Roo.each(this.barItems, function(i){
30096             i.el.setStyle('width', width + '%');
30097             i.topEl.el.setStyle('width', width + '%');
30098             i.bottomEl.el.setStyle('width', width + '%');
30099         }, this);
30100         
30101     }
30102     
30103 });
30104 /*
30105  * - LGPL
30106  *
30107  * Nav Progress Item
30108  * 
30109  */
30110
30111 /**
30112  * @class Roo.bootstrap.NavProgressItem
30113  * @extends Roo.bootstrap.Component
30114  * Bootstrap NavProgressItem class
30115  * @cfg {String} rid the reference id
30116  * @cfg {Boolean} active (true|false) Is item active default false
30117  * @cfg {Boolean} disabled (true|false) Is item active default false
30118  * @cfg {String} html
30119  * @cfg {String} position (top|bottom) text position default bottom
30120  * @cfg {String} icon show icon instead of number
30121  * 
30122  * @constructor
30123  * Create a new NavProgressItem
30124  * @param {Object} config The config object
30125  */
30126 Roo.bootstrap.NavProgressItem = function(config){
30127     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30128     this.addEvents({
30129         // raw events
30130         /**
30131          * @event click
30132          * The raw click event for the entire grid.
30133          * @param {Roo.bootstrap.NavProgressItem} this
30134          * @param {Roo.EventObject} e
30135          */
30136         "click" : true
30137     });
30138    
30139 };
30140
30141 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30142     
30143     rid : '',
30144     active : false,
30145     disabled : false,
30146     html : '',
30147     position : 'bottom',
30148     icon : false,
30149     
30150     getAutoCreate : function()
30151     {
30152         var iconCls = 'roo-navigation-bar-item-icon';
30153         
30154         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30155         
30156         var cfg = {
30157             tag: 'li',
30158             cls: 'roo-navigation-bar-item',
30159             cn : [
30160                 {
30161                     tag : 'i',
30162                     cls : iconCls
30163                 }
30164             ]
30165         };
30166         
30167         if(this.active){
30168             cfg.cls += ' active';
30169         }
30170         if(this.disabled){
30171             cfg.cls += ' disabled';
30172         }
30173         
30174         return cfg;
30175     },
30176     
30177     disable : function()
30178     {
30179         this.setDisabled(true);
30180     },
30181     
30182     enable : function()
30183     {
30184         this.setDisabled(false);
30185     },
30186     
30187     initEvents: function() 
30188     {
30189         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30190         
30191         this.iconEl.on('click', this.onClick, this);
30192     },
30193     
30194     onClick : function(e)
30195     {
30196         e.preventDefault();
30197         
30198         if(this.disabled){
30199             return;
30200         }
30201         
30202         if(this.fireEvent('click', this, e) === false){
30203             return;
30204         };
30205         
30206         this.parent().setActiveItem(this);
30207     },
30208     
30209     isActive: function () 
30210     {
30211         return this.active;
30212     },
30213     
30214     setActive : function(state)
30215     {
30216         if(this.active == state){
30217             return;
30218         }
30219         
30220         this.active = state;
30221         
30222         if (state) {
30223             this.el.addClass('active');
30224             return;
30225         }
30226         
30227         this.el.removeClass('active');
30228         
30229         return;
30230     },
30231     
30232     setDisabled : function(state)
30233     {
30234         if(this.disabled == state){
30235             return;
30236         }
30237         
30238         this.disabled = state;
30239         
30240         if (state) {
30241             this.el.addClass('disabled');
30242             return;
30243         }
30244         
30245         this.el.removeClass('disabled');
30246     },
30247     
30248     tooltipEl : function()
30249     {
30250         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30251     }
30252 });
30253  
30254
30255  /*
30256  * - LGPL
30257  *
30258  * FieldLabel
30259  * 
30260  */
30261
30262 /**
30263  * @class Roo.bootstrap.FieldLabel
30264  * @extends Roo.bootstrap.Component
30265  * Bootstrap FieldLabel class
30266  * @cfg {String} html contents of the element
30267  * @cfg {String} tag tag of the element default label
30268  * @cfg {String} cls class of the element
30269  * @cfg {String} target label target 
30270  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30271  * @cfg {String} invalidClass default "text-warning"
30272  * @cfg {String} validClass default "text-success"
30273  * @cfg {String} iconTooltip default "This field is required"
30274  * @cfg {String} indicatorpos (left|right) default left
30275  * 
30276  * @constructor
30277  * Create a new FieldLabel
30278  * @param {Object} config The config object
30279  */
30280
30281 Roo.bootstrap.FieldLabel = function(config){
30282     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30283     
30284     this.addEvents({
30285             /**
30286              * @event invalid
30287              * Fires after the field has been marked as invalid.
30288              * @param {Roo.form.FieldLabel} this
30289              * @param {String} msg The validation message
30290              */
30291             invalid : true,
30292             /**
30293              * @event valid
30294              * Fires after the field has been validated with no errors.
30295              * @param {Roo.form.FieldLabel} this
30296              */
30297             valid : true
30298         });
30299 };
30300
30301 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30302     
30303     tag: 'label',
30304     cls: '',
30305     html: '',
30306     target: '',
30307     allowBlank : true,
30308     invalidClass : 'has-warning',
30309     validClass : 'has-success',
30310     iconTooltip : 'This field is required',
30311     indicatorpos : 'left',
30312     
30313     getAutoCreate : function(){
30314         
30315         var cls = "";
30316         if (!this.allowBlank) {
30317             cls  = "visible";
30318         }
30319         
30320         var cfg = {
30321             tag : this.tag,
30322             cls : 'roo-bootstrap-field-label ' + this.cls,
30323             for : this.target,
30324             cn : [
30325                 {
30326                     tag : 'i',
30327                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30328                     tooltip : this.iconTooltip
30329                 },
30330                 {
30331                     tag : 'span',
30332                     html : this.html
30333                 }
30334             ] 
30335         };
30336         
30337         if(this.indicatorpos == 'right'){
30338             var cfg = {
30339                 tag : this.tag,
30340                 cls : 'roo-bootstrap-field-label ' + this.cls,
30341                 for : this.target,
30342                 cn : [
30343                     {
30344                         tag : 'span',
30345                         html : this.html
30346                     },
30347                     {
30348                         tag : 'i',
30349                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30350                         tooltip : this.iconTooltip
30351                     }
30352                 ] 
30353             };
30354         }
30355         
30356         return cfg;
30357     },
30358     
30359     initEvents: function() 
30360     {
30361         Roo.bootstrap.Element.superclass.initEvents.call(this);
30362         
30363         this.indicator = this.indicatorEl();
30364         
30365         if(this.indicator){
30366             this.indicator.removeClass('visible');
30367             this.indicator.addClass('invisible');
30368         }
30369         
30370         Roo.bootstrap.FieldLabel.register(this);
30371     },
30372     
30373     indicatorEl : function()
30374     {
30375         var indicator = this.el.select('i.roo-required-indicator',true).first();
30376         
30377         if(!indicator){
30378             return false;
30379         }
30380         
30381         return indicator;
30382         
30383     },
30384     
30385     /**
30386      * Mark this field as valid
30387      */
30388     markValid : function()
30389     {
30390         if(this.indicator){
30391             this.indicator.removeClass('visible');
30392             this.indicator.addClass('invisible');
30393         }
30394         
30395         this.el.removeClass(this.invalidClass);
30396         
30397         this.el.addClass(this.validClass);
30398         
30399         this.fireEvent('valid', this);
30400     },
30401     
30402     /**
30403      * Mark this field as invalid
30404      * @param {String} msg The validation message
30405      */
30406     markInvalid : function(msg)
30407     {
30408         if(this.indicator){
30409             this.indicator.removeClass('invisible');
30410             this.indicator.addClass('visible');
30411         }
30412         
30413         this.el.removeClass(this.validClass);
30414         
30415         this.el.addClass(this.invalidClass);
30416         
30417         this.fireEvent('invalid', this, msg);
30418     }
30419     
30420    
30421 });
30422
30423 Roo.apply(Roo.bootstrap.FieldLabel, {
30424     
30425     groups: {},
30426     
30427      /**
30428     * register a FieldLabel Group
30429     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30430     */
30431     register : function(label)
30432     {
30433         if(this.groups.hasOwnProperty(label.target)){
30434             return;
30435         }
30436      
30437         this.groups[label.target] = label;
30438         
30439     },
30440     /**
30441     * fetch a FieldLabel Group based on the target
30442     * @param {string} target
30443     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30444     */
30445     get: function(target) {
30446         if (typeof(this.groups[target]) == 'undefined') {
30447             return false;
30448         }
30449         
30450         return this.groups[target] ;
30451     }
30452 });
30453
30454  
30455
30456  /*
30457  * - LGPL
30458  *
30459  * page DateSplitField.
30460  * 
30461  */
30462
30463
30464 /**
30465  * @class Roo.bootstrap.DateSplitField
30466  * @extends Roo.bootstrap.Component
30467  * Bootstrap DateSplitField class
30468  * @cfg {string} fieldLabel - the label associated
30469  * @cfg {Number} labelWidth set the width of label (0-12)
30470  * @cfg {String} labelAlign (top|left)
30471  * @cfg {Boolean} dayAllowBlank (true|false) default false
30472  * @cfg {Boolean} monthAllowBlank (true|false) default false
30473  * @cfg {Boolean} yearAllowBlank (true|false) default false
30474  * @cfg {string} dayPlaceholder 
30475  * @cfg {string} monthPlaceholder
30476  * @cfg {string} yearPlaceholder
30477  * @cfg {string} dayFormat default 'd'
30478  * @cfg {string} monthFormat default 'm'
30479  * @cfg {string} yearFormat default 'Y'
30480  * @cfg {Number} labellg set the width of label (1-12)
30481  * @cfg {Number} labelmd set the width of label (1-12)
30482  * @cfg {Number} labelsm set the width of label (1-12)
30483  * @cfg {Number} labelxs set the width of label (1-12)
30484
30485  *     
30486  * @constructor
30487  * Create a new DateSplitField
30488  * @param {Object} config The config object
30489  */
30490
30491 Roo.bootstrap.DateSplitField = function(config){
30492     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30493     
30494     this.addEvents({
30495         // raw events
30496          /**
30497          * @event years
30498          * getting the data of years
30499          * @param {Roo.bootstrap.DateSplitField} this
30500          * @param {Object} years
30501          */
30502         "years" : true,
30503         /**
30504          * @event days
30505          * getting the data of days
30506          * @param {Roo.bootstrap.DateSplitField} this
30507          * @param {Object} days
30508          */
30509         "days" : true,
30510         /**
30511          * @event invalid
30512          * Fires after the field has been marked as invalid.
30513          * @param {Roo.form.Field} this
30514          * @param {String} msg The validation message
30515          */
30516         invalid : true,
30517        /**
30518          * @event valid
30519          * Fires after the field has been validated with no errors.
30520          * @param {Roo.form.Field} this
30521          */
30522         valid : true
30523     });
30524 };
30525
30526 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30527     
30528     fieldLabel : '',
30529     labelAlign : 'top',
30530     labelWidth : 3,
30531     dayAllowBlank : false,
30532     monthAllowBlank : false,
30533     yearAllowBlank : false,
30534     dayPlaceholder : '',
30535     monthPlaceholder : '',
30536     yearPlaceholder : '',
30537     dayFormat : 'd',
30538     monthFormat : 'm',
30539     yearFormat : 'Y',
30540     isFormField : true,
30541     labellg : 0,
30542     labelmd : 0,
30543     labelsm : 0,
30544     labelxs : 0,
30545     
30546     getAutoCreate : function()
30547     {
30548         var cfg = {
30549             tag : 'div',
30550             cls : 'row roo-date-split-field-group',
30551             cn : [
30552                 {
30553                     tag : 'input',
30554                     type : 'hidden',
30555                     cls : 'form-hidden-field roo-date-split-field-group-value',
30556                     name : this.name
30557                 }
30558             ]
30559         };
30560         
30561         var labelCls = 'col-md-12';
30562         var contentCls = 'col-md-4';
30563         
30564         if(this.fieldLabel){
30565             
30566             var label = {
30567                 tag : 'div',
30568                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30569                 cn : [
30570                     {
30571                         tag : 'label',
30572                         html : this.fieldLabel
30573                     }
30574                 ]
30575             };
30576             
30577             if(this.labelAlign == 'left'){
30578             
30579                 if(this.labelWidth > 12){
30580                     label.style = "width: " + this.labelWidth + 'px';
30581                 }
30582
30583                 if(this.labelWidth < 13 && this.labelmd == 0){
30584                     this.labelmd = this.labelWidth;
30585                 }
30586
30587                 if(this.labellg > 0){
30588                     labelCls = ' col-lg-' + this.labellg;
30589                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30590                 }
30591
30592                 if(this.labelmd > 0){
30593                     labelCls = ' col-md-' + this.labelmd;
30594                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30595                 }
30596
30597                 if(this.labelsm > 0){
30598                     labelCls = ' col-sm-' + this.labelsm;
30599                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30600                 }
30601
30602                 if(this.labelxs > 0){
30603                     labelCls = ' col-xs-' + this.labelxs;
30604                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30605                 }
30606             }
30607             
30608             label.cls += ' ' + labelCls;
30609             
30610             cfg.cn.push(label);
30611         }
30612         
30613         Roo.each(['day', 'month', 'year'], function(t){
30614             cfg.cn.push({
30615                 tag : 'div',
30616                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30617             });
30618         }, this);
30619         
30620         return cfg;
30621     },
30622     
30623     inputEl: function ()
30624     {
30625         return this.el.select('.roo-date-split-field-group-value', true).first();
30626     },
30627     
30628     onRender : function(ct, position) 
30629     {
30630         var _this = this;
30631         
30632         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30633         
30634         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30635         
30636         this.dayField = new Roo.bootstrap.ComboBox({
30637             allowBlank : this.dayAllowBlank,
30638             alwaysQuery : true,
30639             displayField : 'value',
30640             editable : false,
30641             fieldLabel : '',
30642             forceSelection : true,
30643             mode : 'local',
30644             placeholder : this.dayPlaceholder,
30645             selectOnFocus : true,
30646             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30647             triggerAction : 'all',
30648             typeAhead : true,
30649             valueField : 'value',
30650             store : new Roo.data.SimpleStore({
30651                 data : (function() {    
30652                     var days = [];
30653                     _this.fireEvent('days', _this, days);
30654                     return days;
30655                 })(),
30656                 fields : [ 'value' ]
30657             }),
30658             listeners : {
30659                 select : function (_self, record, index)
30660                 {
30661                     _this.setValue(_this.getValue());
30662                 }
30663             }
30664         });
30665
30666         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30667         
30668         this.monthField = new Roo.bootstrap.MonthField({
30669             after : '<i class=\"fa fa-calendar\"></i>',
30670             allowBlank : this.monthAllowBlank,
30671             placeholder : this.monthPlaceholder,
30672             readOnly : true,
30673             listeners : {
30674                 render : function (_self)
30675                 {
30676                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30677                         e.preventDefault();
30678                         _self.focus();
30679                     });
30680                 },
30681                 select : function (_self, oldvalue, newvalue)
30682                 {
30683                     _this.setValue(_this.getValue());
30684                 }
30685             }
30686         });
30687         
30688         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30689         
30690         this.yearField = new Roo.bootstrap.ComboBox({
30691             allowBlank : this.yearAllowBlank,
30692             alwaysQuery : true,
30693             displayField : 'value',
30694             editable : false,
30695             fieldLabel : '',
30696             forceSelection : true,
30697             mode : 'local',
30698             placeholder : this.yearPlaceholder,
30699             selectOnFocus : true,
30700             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30701             triggerAction : 'all',
30702             typeAhead : true,
30703             valueField : 'value',
30704             store : new Roo.data.SimpleStore({
30705                 data : (function() {
30706                     var years = [];
30707                     _this.fireEvent('years', _this, years);
30708                     return years;
30709                 })(),
30710                 fields : [ 'value' ]
30711             }),
30712             listeners : {
30713                 select : function (_self, record, index)
30714                 {
30715                     _this.setValue(_this.getValue());
30716                 }
30717             }
30718         });
30719
30720         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30721     },
30722     
30723     setValue : function(v, format)
30724     {
30725         this.inputEl.dom.value = v;
30726         
30727         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30728         
30729         var d = Date.parseDate(v, f);
30730         
30731         if(!d){
30732             this.validate();
30733             return;
30734         }
30735         
30736         this.setDay(d.format(this.dayFormat));
30737         this.setMonth(d.format(this.monthFormat));
30738         this.setYear(d.format(this.yearFormat));
30739         
30740         this.validate();
30741         
30742         return;
30743     },
30744     
30745     setDay : function(v)
30746     {
30747         this.dayField.setValue(v);
30748         this.inputEl.dom.value = this.getValue();
30749         this.validate();
30750         return;
30751     },
30752     
30753     setMonth : function(v)
30754     {
30755         this.monthField.setValue(v, true);
30756         this.inputEl.dom.value = this.getValue();
30757         this.validate();
30758         return;
30759     },
30760     
30761     setYear : function(v)
30762     {
30763         this.yearField.setValue(v);
30764         this.inputEl.dom.value = this.getValue();
30765         this.validate();
30766         return;
30767     },
30768     
30769     getDay : function()
30770     {
30771         return this.dayField.getValue();
30772     },
30773     
30774     getMonth : function()
30775     {
30776         return this.monthField.getValue();
30777     },
30778     
30779     getYear : function()
30780     {
30781         return this.yearField.getValue();
30782     },
30783     
30784     getValue : function()
30785     {
30786         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30787         
30788         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30789         
30790         return date;
30791     },
30792     
30793     reset : function()
30794     {
30795         this.setDay('');
30796         this.setMonth('');
30797         this.setYear('');
30798         this.inputEl.dom.value = '';
30799         this.validate();
30800         return;
30801     },
30802     
30803     validate : function()
30804     {
30805         var d = this.dayField.validate();
30806         var m = this.monthField.validate();
30807         var y = this.yearField.validate();
30808         
30809         var valid = true;
30810         
30811         if(
30812                 (!this.dayAllowBlank && !d) ||
30813                 (!this.monthAllowBlank && !m) ||
30814                 (!this.yearAllowBlank && !y)
30815         ){
30816             valid = false;
30817         }
30818         
30819         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30820             return valid;
30821         }
30822         
30823         if(valid){
30824             this.markValid();
30825             return valid;
30826         }
30827         
30828         this.markInvalid();
30829         
30830         return valid;
30831     },
30832     
30833     markValid : function()
30834     {
30835         
30836         var label = this.el.select('label', true).first();
30837         var icon = this.el.select('i.fa-star', true).first();
30838
30839         if(label && icon){
30840             icon.remove();
30841         }
30842         
30843         this.fireEvent('valid', this);
30844     },
30845     
30846      /**
30847      * Mark this field as invalid
30848      * @param {String} msg The validation message
30849      */
30850     markInvalid : function(msg)
30851     {
30852         
30853         var label = this.el.select('label', true).first();
30854         var icon = this.el.select('i.fa-star', true).first();
30855
30856         if(label && !icon){
30857             this.el.select('.roo-date-split-field-label', true).createChild({
30858                 tag : 'i',
30859                 cls : 'text-danger fa fa-lg fa-star',
30860                 tooltip : 'This field is required',
30861                 style : 'margin-right:5px;'
30862             }, label, true);
30863         }
30864         
30865         this.fireEvent('invalid', this, msg);
30866     },
30867     
30868     clearInvalid : function()
30869     {
30870         var label = this.el.select('label', true).first();
30871         var icon = this.el.select('i.fa-star', true).first();
30872
30873         if(label && icon){
30874             icon.remove();
30875         }
30876         
30877         this.fireEvent('valid', this);
30878     },
30879     
30880     getName: function()
30881     {
30882         return this.name;
30883     }
30884     
30885 });
30886
30887  /**
30888  *
30889  * This is based on 
30890  * http://masonry.desandro.com
30891  *
30892  * The idea is to render all the bricks based on vertical width...
30893  *
30894  * The original code extends 'outlayer' - we might need to use that....
30895  * 
30896  */
30897
30898
30899 /**
30900  * @class Roo.bootstrap.LayoutMasonry
30901  * @extends Roo.bootstrap.Component
30902  * Bootstrap Layout Masonry class
30903  * 
30904  * @constructor
30905  * Create a new Element
30906  * @param {Object} config The config object
30907  */
30908
30909 Roo.bootstrap.LayoutMasonry = function(config){
30910     
30911     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30912     
30913     this.bricks = [];
30914     
30915     Roo.bootstrap.LayoutMasonry.register(this);
30916     
30917     this.addEvents({
30918         // raw events
30919         /**
30920          * @event layout
30921          * Fire after layout the items
30922          * @param {Roo.bootstrap.LayoutMasonry} this
30923          * @param {Roo.EventObject} e
30924          */
30925         "layout" : true
30926     });
30927     
30928 };
30929
30930 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30931     
30932     /**
30933      * @cfg {Boolean} isLayoutInstant = no animation?
30934      */   
30935     isLayoutInstant : false, // needed?
30936    
30937     /**
30938      * @cfg {Number} boxWidth  width of the columns
30939      */   
30940     boxWidth : 450,
30941     
30942       /**
30943      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30944      */   
30945     boxHeight : 0,
30946     
30947     /**
30948      * @cfg {Number} padWidth padding below box..
30949      */   
30950     padWidth : 10, 
30951     
30952     /**
30953      * @cfg {Number} gutter gutter width..
30954      */   
30955     gutter : 10,
30956     
30957      /**
30958      * @cfg {Number} maxCols maximum number of columns
30959      */   
30960     
30961     maxCols: 0,
30962     
30963     /**
30964      * @cfg {Boolean} isAutoInitial defalut true
30965      */   
30966     isAutoInitial : true, 
30967     
30968     containerWidth: 0,
30969     
30970     /**
30971      * @cfg {Boolean} isHorizontal defalut false
30972      */   
30973     isHorizontal : false, 
30974
30975     currentSize : null,
30976     
30977     tag: 'div',
30978     
30979     cls: '',
30980     
30981     bricks: null, //CompositeElement
30982     
30983     cols : 1,
30984     
30985     _isLayoutInited : false,
30986     
30987 //    isAlternative : false, // only use for vertical layout...
30988     
30989     /**
30990      * @cfg {Number} alternativePadWidth padding below box..
30991      */   
30992     alternativePadWidth : 50,
30993     
30994     selectedBrick : [],
30995     
30996     getAutoCreate : function(){
30997         
30998         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30999         
31000         var cfg = {
31001             tag: this.tag,
31002             cls: 'blog-masonary-wrapper ' + this.cls,
31003             cn : {
31004                 cls : 'mas-boxes masonary'
31005             }
31006         };
31007         
31008         return cfg;
31009     },
31010     
31011     getChildContainer: function( )
31012     {
31013         if (this.boxesEl) {
31014             return this.boxesEl;
31015         }
31016         
31017         this.boxesEl = this.el.select('.mas-boxes').first();
31018         
31019         return this.boxesEl;
31020     },
31021     
31022     
31023     initEvents : function()
31024     {
31025         var _this = this;
31026         
31027         if(this.isAutoInitial){
31028             Roo.log('hook children rendered');
31029             this.on('childrenrendered', function() {
31030                 Roo.log('children rendered');
31031                 _this.initial();
31032             } ,this);
31033         }
31034     },
31035     
31036     initial : function()
31037     {
31038         this.selectedBrick = [];
31039         
31040         this.currentSize = this.el.getBox(true);
31041         
31042         Roo.EventManager.onWindowResize(this.resize, this); 
31043
31044         if(!this.isAutoInitial){
31045             this.layout();
31046             return;
31047         }
31048         
31049         this.layout();
31050         
31051         return;
31052         //this.layout.defer(500,this);
31053         
31054     },
31055     
31056     resize : function()
31057     {
31058         var cs = this.el.getBox(true);
31059         
31060         if (
31061                 this.currentSize.width == cs.width && 
31062                 this.currentSize.x == cs.x && 
31063                 this.currentSize.height == cs.height && 
31064                 this.currentSize.y == cs.y 
31065         ) {
31066             Roo.log("no change in with or X or Y");
31067             return;
31068         }
31069         
31070         this.currentSize = cs;
31071         
31072         this.layout();
31073         
31074     },
31075     
31076     layout : function()
31077     {   
31078         this._resetLayout();
31079         
31080         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31081         
31082         this.layoutItems( isInstant );
31083       
31084         this._isLayoutInited = true;
31085         
31086         this.fireEvent('layout', this);
31087         
31088     },
31089     
31090     _resetLayout : function()
31091     {
31092         if(this.isHorizontal){
31093             this.horizontalMeasureColumns();
31094             return;
31095         }
31096         
31097         this.verticalMeasureColumns();
31098         
31099     },
31100     
31101     verticalMeasureColumns : function()
31102     {
31103         this.getContainerWidth();
31104         
31105 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31106 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31107 //            return;
31108 //        }
31109         
31110         var boxWidth = this.boxWidth + this.padWidth;
31111         
31112         if(this.containerWidth < this.boxWidth){
31113             boxWidth = this.containerWidth
31114         }
31115         
31116         var containerWidth = this.containerWidth;
31117         
31118         var cols = Math.floor(containerWidth / boxWidth);
31119         
31120         this.cols = Math.max( cols, 1 );
31121         
31122         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31123         
31124         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31125         
31126         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31127         
31128         this.colWidth = boxWidth + avail - this.padWidth;
31129         
31130         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31131         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31132     },
31133     
31134     horizontalMeasureColumns : function()
31135     {
31136         this.getContainerWidth();
31137         
31138         var boxWidth = this.boxWidth;
31139         
31140         if(this.containerWidth < boxWidth){
31141             boxWidth = this.containerWidth;
31142         }
31143         
31144         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31145         
31146         this.el.setHeight(boxWidth);
31147         
31148     },
31149     
31150     getContainerWidth : function()
31151     {
31152         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31153     },
31154     
31155     layoutItems : function( isInstant )
31156     {
31157         Roo.log(this.bricks);
31158         
31159         var items = Roo.apply([], this.bricks);
31160         
31161         if(this.isHorizontal){
31162             this._horizontalLayoutItems( items , isInstant );
31163             return;
31164         }
31165         
31166 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31167 //            this._verticalAlternativeLayoutItems( items , isInstant );
31168 //            return;
31169 //        }
31170         
31171         this._verticalLayoutItems( items , isInstant );
31172         
31173     },
31174     
31175     _verticalLayoutItems : function ( items , isInstant)
31176     {
31177         if ( !items || !items.length ) {
31178             return;
31179         }
31180         
31181         var standard = [
31182             ['xs', 'xs', 'xs', 'tall'],
31183             ['xs', 'xs', 'tall'],
31184             ['xs', 'xs', 'sm'],
31185             ['xs', 'xs', 'xs'],
31186             ['xs', 'tall'],
31187             ['xs', 'sm'],
31188             ['xs', 'xs'],
31189             ['xs'],
31190             
31191             ['sm', 'xs', 'xs'],
31192             ['sm', 'xs'],
31193             ['sm'],
31194             
31195             ['tall', 'xs', 'xs', 'xs'],
31196             ['tall', 'xs', 'xs'],
31197             ['tall', 'xs'],
31198             ['tall']
31199             
31200         ];
31201         
31202         var queue = [];
31203         
31204         var boxes = [];
31205         
31206         var box = [];
31207         
31208         Roo.each(items, function(item, k){
31209             
31210             switch (item.size) {
31211                 // these layouts take up a full box,
31212                 case 'md' :
31213                 case 'md-left' :
31214                 case 'md-right' :
31215                 case 'wide' :
31216                     
31217                     if(box.length){
31218                         boxes.push(box);
31219                         box = [];
31220                     }
31221                     
31222                     boxes.push([item]);
31223                     
31224                     break;
31225                     
31226                 case 'xs' :
31227                 case 'sm' :
31228                 case 'tall' :
31229                     
31230                     box.push(item);
31231                     
31232                     break;
31233                 default :
31234                     break;
31235                     
31236             }
31237             
31238         }, this);
31239         
31240         if(box.length){
31241             boxes.push(box);
31242             box = [];
31243         }
31244         
31245         var filterPattern = function(box, length)
31246         {
31247             if(!box.length){
31248                 return;
31249             }
31250             
31251             var match = false;
31252             
31253             var pattern = box.slice(0, length);
31254             
31255             var format = [];
31256             
31257             Roo.each(pattern, function(i){
31258                 format.push(i.size);
31259             }, this);
31260             
31261             Roo.each(standard, function(s){
31262                 
31263                 if(String(s) != String(format)){
31264                     return;
31265                 }
31266                 
31267                 match = true;
31268                 return false;
31269                 
31270             }, this);
31271             
31272             if(!match && length == 1){
31273                 return;
31274             }
31275             
31276             if(!match){
31277                 filterPattern(box, length - 1);
31278                 return;
31279             }
31280                 
31281             queue.push(pattern);
31282
31283             box = box.slice(length, box.length);
31284
31285             filterPattern(box, 4);
31286
31287             return;
31288             
31289         }
31290         
31291         Roo.each(boxes, function(box, k){
31292             
31293             if(!box.length){
31294                 return;
31295             }
31296             
31297             if(box.length == 1){
31298                 queue.push(box);
31299                 return;
31300             }
31301             
31302             filterPattern(box, 4);
31303             
31304         }, this);
31305         
31306         this._processVerticalLayoutQueue( queue, isInstant );
31307         
31308     },
31309     
31310 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31311 //    {
31312 //        if ( !items || !items.length ) {
31313 //            return;
31314 //        }
31315 //
31316 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31317 //        
31318 //    },
31319     
31320     _horizontalLayoutItems : function ( items , isInstant)
31321     {
31322         if ( !items || !items.length || items.length < 3) {
31323             return;
31324         }
31325         
31326         items.reverse();
31327         
31328         var eItems = items.slice(0, 3);
31329         
31330         items = items.slice(3, items.length);
31331         
31332         var standard = [
31333             ['xs', 'xs', 'xs', 'wide'],
31334             ['xs', 'xs', 'wide'],
31335             ['xs', 'xs', 'sm'],
31336             ['xs', 'xs', 'xs'],
31337             ['xs', 'wide'],
31338             ['xs', 'sm'],
31339             ['xs', 'xs'],
31340             ['xs'],
31341             
31342             ['sm', 'xs', 'xs'],
31343             ['sm', 'xs'],
31344             ['sm'],
31345             
31346             ['wide', 'xs', 'xs', 'xs'],
31347             ['wide', 'xs', 'xs'],
31348             ['wide', 'xs'],
31349             ['wide'],
31350             
31351             ['wide-thin']
31352         ];
31353         
31354         var queue = [];
31355         
31356         var boxes = [];
31357         
31358         var box = [];
31359         
31360         Roo.each(items, function(item, k){
31361             
31362             switch (item.size) {
31363                 case 'md' :
31364                 case 'md-left' :
31365                 case 'md-right' :
31366                 case 'tall' :
31367                     
31368                     if(box.length){
31369                         boxes.push(box);
31370                         box = [];
31371                     }
31372                     
31373                     boxes.push([item]);
31374                     
31375                     break;
31376                     
31377                 case 'xs' :
31378                 case 'sm' :
31379                 case 'wide' :
31380                 case 'wide-thin' :
31381                     
31382                     box.push(item);
31383                     
31384                     break;
31385                 default :
31386                     break;
31387                     
31388             }
31389             
31390         }, this);
31391         
31392         if(box.length){
31393             boxes.push(box);
31394             box = [];
31395         }
31396         
31397         var filterPattern = function(box, length)
31398         {
31399             if(!box.length){
31400                 return;
31401             }
31402             
31403             var match = false;
31404             
31405             var pattern = box.slice(0, length);
31406             
31407             var format = [];
31408             
31409             Roo.each(pattern, function(i){
31410                 format.push(i.size);
31411             }, this);
31412             
31413             Roo.each(standard, function(s){
31414                 
31415                 if(String(s) != String(format)){
31416                     return;
31417                 }
31418                 
31419                 match = true;
31420                 return false;
31421                 
31422             }, this);
31423             
31424             if(!match && length == 1){
31425                 return;
31426             }
31427             
31428             if(!match){
31429                 filterPattern(box, length - 1);
31430                 return;
31431             }
31432                 
31433             queue.push(pattern);
31434
31435             box = box.slice(length, box.length);
31436
31437             filterPattern(box, 4);
31438
31439             return;
31440             
31441         }
31442         
31443         Roo.each(boxes, function(box, k){
31444             
31445             if(!box.length){
31446                 return;
31447             }
31448             
31449             if(box.length == 1){
31450                 queue.push(box);
31451                 return;
31452             }
31453             
31454             filterPattern(box, 4);
31455             
31456         }, this);
31457         
31458         
31459         var prune = [];
31460         
31461         var pos = this.el.getBox(true);
31462         
31463         var minX = pos.x;
31464         
31465         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31466         
31467         var hit_end = false;
31468         
31469         Roo.each(queue, function(box){
31470             
31471             if(hit_end){
31472                 
31473                 Roo.each(box, function(b){
31474                 
31475                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31476                     b.el.hide();
31477
31478                 }, this);
31479
31480                 return;
31481             }
31482             
31483             var mx = 0;
31484             
31485             Roo.each(box, function(b){
31486                 
31487                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31488                 b.el.show();
31489
31490                 mx = Math.max(mx, b.x);
31491                 
31492             }, this);
31493             
31494             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31495             
31496             if(maxX < minX){
31497                 
31498                 Roo.each(box, function(b){
31499                 
31500                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31501                     b.el.hide();
31502                     
31503                 }, this);
31504                 
31505                 hit_end = true;
31506                 
31507                 return;
31508             }
31509             
31510             prune.push(box);
31511             
31512         }, this);
31513         
31514         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31515     },
31516     
31517     /** Sets position of item in DOM
31518     * @param {Element} item
31519     * @param {Number} x - horizontal position
31520     * @param {Number} y - vertical position
31521     * @param {Boolean} isInstant - disables transitions
31522     */
31523     _processVerticalLayoutQueue : function( queue, isInstant )
31524     {
31525         var pos = this.el.getBox(true);
31526         var x = pos.x;
31527         var y = pos.y;
31528         var maxY = [];
31529         
31530         for (var i = 0; i < this.cols; i++){
31531             maxY[i] = pos.y;
31532         }
31533         
31534         Roo.each(queue, function(box, k){
31535             
31536             var col = k % this.cols;
31537             
31538             Roo.each(box, function(b,kk){
31539                 
31540                 b.el.position('absolute');
31541                 
31542                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31543                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31544                 
31545                 if(b.size == 'md-left' || b.size == 'md-right'){
31546                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31547                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31548                 }
31549                 
31550                 b.el.setWidth(width);
31551                 b.el.setHeight(height);
31552                 // iframe?
31553                 b.el.select('iframe',true).setSize(width,height);
31554                 
31555             }, this);
31556             
31557             for (var i = 0; i < this.cols; i++){
31558                 
31559                 if(maxY[i] < maxY[col]){
31560                     col = i;
31561                     continue;
31562                 }
31563                 
31564                 col = Math.min(col, i);
31565                 
31566             }
31567             
31568             x = pos.x + col * (this.colWidth + this.padWidth);
31569             
31570             y = maxY[col];
31571             
31572             var positions = [];
31573             
31574             switch (box.length){
31575                 case 1 :
31576                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31577                     break;
31578                 case 2 :
31579                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31580                     break;
31581                 case 3 :
31582                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31583                     break;
31584                 case 4 :
31585                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31586                     break;
31587                 default :
31588                     break;
31589             }
31590             
31591             Roo.each(box, function(b,kk){
31592                 
31593                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31594                 
31595                 var sz = b.el.getSize();
31596                 
31597                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31598                 
31599             }, this);
31600             
31601         }, this);
31602         
31603         var mY = 0;
31604         
31605         for (var i = 0; i < this.cols; i++){
31606             mY = Math.max(mY, maxY[i]);
31607         }
31608         
31609         this.el.setHeight(mY - pos.y);
31610         
31611     },
31612     
31613 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31614 //    {
31615 //        var pos = this.el.getBox(true);
31616 //        var x = pos.x;
31617 //        var y = pos.y;
31618 //        var maxX = pos.right;
31619 //        
31620 //        var maxHeight = 0;
31621 //        
31622 //        Roo.each(items, function(item, k){
31623 //            
31624 //            var c = k % 2;
31625 //            
31626 //            item.el.position('absolute');
31627 //                
31628 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31629 //
31630 //            item.el.setWidth(width);
31631 //
31632 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31633 //
31634 //            item.el.setHeight(height);
31635 //            
31636 //            if(c == 0){
31637 //                item.el.setXY([x, y], isInstant ? false : true);
31638 //            } else {
31639 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31640 //            }
31641 //            
31642 //            y = y + height + this.alternativePadWidth;
31643 //            
31644 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31645 //            
31646 //        }, this);
31647 //        
31648 //        this.el.setHeight(maxHeight);
31649 //        
31650 //    },
31651     
31652     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31653     {
31654         var pos = this.el.getBox(true);
31655         
31656         var minX = pos.x;
31657         var minY = pos.y;
31658         
31659         var maxX = pos.right;
31660         
31661         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31662         
31663         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31664         
31665         Roo.each(queue, function(box, k){
31666             
31667             Roo.each(box, function(b, kk){
31668                 
31669                 b.el.position('absolute');
31670                 
31671                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31672                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31673                 
31674                 if(b.size == 'md-left' || b.size == 'md-right'){
31675                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31676                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31677                 }
31678                 
31679                 b.el.setWidth(width);
31680                 b.el.setHeight(height);
31681                 
31682             }, this);
31683             
31684             if(!box.length){
31685                 return;
31686             }
31687             
31688             var positions = [];
31689             
31690             switch (box.length){
31691                 case 1 :
31692                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31693                     break;
31694                 case 2 :
31695                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31696                     break;
31697                 case 3 :
31698                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31699                     break;
31700                 case 4 :
31701                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31702                     break;
31703                 default :
31704                     break;
31705             }
31706             
31707             Roo.each(box, function(b,kk){
31708                 
31709                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31710                 
31711                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31712                 
31713             }, this);
31714             
31715         }, this);
31716         
31717     },
31718     
31719     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31720     {
31721         Roo.each(eItems, function(b,k){
31722             
31723             b.size = (k == 0) ? 'sm' : 'xs';
31724             b.x = (k == 0) ? 2 : 1;
31725             b.y = (k == 0) ? 2 : 1;
31726             
31727             b.el.position('absolute');
31728             
31729             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31730                 
31731             b.el.setWidth(width);
31732             
31733             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31734             
31735             b.el.setHeight(height);
31736             
31737         }, this);
31738
31739         var positions = [];
31740         
31741         positions.push({
31742             x : maxX - this.unitWidth * 2 - this.gutter,
31743             y : minY
31744         });
31745         
31746         positions.push({
31747             x : maxX - this.unitWidth,
31748             y : minY + (this.unitWidth + this.gutter) * 2
31749         });
31750         
31751         positions.push({
31752             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31753             y : minY
31754         });
31755         
31756         Roo.each(eItems, function(b,k){
31757             
31758             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31759
31760         }, this);
31761         
31762     },
31763     
31764     getVerticalOneBoxColPositions : function(x, y, box)
31765     {
31766         var pos = [];
31767         
31768         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31769         
31770         if(box[0].size == 'md-left'){
31771             rand = 0;
31772         }
31773         
31774         if(box[0].size == 'md-right'){
31775             rand = 1;
31776         }
31777         
31778         pos.push({
31779             x : x + (this.unitWidth + this.gutter) * rand,
31780             y : y
31781         });
31782         
31783         return pos;
31784     },
31785     
31786     getVerticalTwoBoxColPositions : function(x, y, box)
31787     {
31788         var pos = [];
31789         
31790         if(box[0].size == 'xs'){
31791             
31792             pos.push({
31793                 x : x,
31794                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31795             });
31796
31797             pos.push({
31798                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31799                 y : y
31800             });
31801             
31802             return pos;
31803             
31804         }
31805         
31806         pos.push({
31807             x : x,
31808             y : y
31809         });
31810
31811         pos.push({
31812             x : x + (this.unitWidth + this.gutter) * 2,
31813             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31814         });
31815         
31816         return pos;
31817         
31818     },
31819     
31820     getVerticalThreeBoxColPositions : function(x, y, box)
31821     {
31822         var pos = [];
31823         
31824         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31825             
31826             pos.push({
31827                 x : x,
31828                 y : y
31829             });
31830
31831             pos.push({
31832                 x : x + (this.unitWidth + this.gutter) * 1,
31833                 y : y
31834             });
31835             
31836             pos.push({
31837                 x : x + (this.unitWidth + this.gutter) * 2,
31838                 y : y
31839             });
31840             
31841             return pos;
31842             
31843         }
31844         
31845         if(box[0].size == 'xs' && box[1].size == 'xs'){
31846             
31847             pos.push({
31848                 x : x,
31849                 y : y
31850             });
31851
31852             pos.push({
31853                 x : x,
31854                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31855             });
31856             
31857             pos.push({
31858                 x : x + (this.unitWidth + this.gutter) * 1,
31859                 y : y
31860             });
31861             
31862             return pos;
31863             
31864         }
31865         
31866         pos.push({
31867             x : x,
31868             y : y
31869         });
31870
31871         pos.push({
31872             x : x + (this.unitWidth + this.gutter) * 2,
31873             y : y
31874         });
31875
31876         pos.push({
31877             x : x + (this.unitWidth + this.gutter) * 2,
31878             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31879         });
31880             
31881         return pos;
31882         
31883     },
31884     
31885     getVerticalFourBoxColPositions : function(x, y, box)
31886     {
31887         var pos = [];
31888         
31889         if(box[0].size == 'xs'){
31890             
31891             pos.push({
31892                 x : x,
31893                 y : y
31894             });
31895
31896             pos.push({
31897                 x : x,
31898                 y : y + (this.unitHeight + this.gutter) * 1
31899             });
31900             
31901             pos.push({
31902                 x : x,
31903                 y : y + (this.unitHeight + this.gutter) * 2
31904             });
31905             
31906             pos.push({
31907                 x : x + (this.unitWidth + this.gutter) * 1,
31908                 y : y
31909             });
31910             
31911             return pos;
31912             
31913         }
31914         
31915         pos.push({
31916             x : x,
31917             y : y
31918         });
31919
31920         pos.push({
31921             x : x + (this.unitWidth + this.gutter) * 2,
31922             y : y
31923         });
31924
31925         pos.push({
31926             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31927             y : y + (this.unitHeight + this.gutter) * 1
31928         });
31929
31930         pos.push({
31931             x : x + (this.unitWidth + this.gutter) * 2,
31932             y : y + (this.unitWidth + this.gutter) * 2
31933         });
31934
31935         return pos;
31936         
31937     },
31938     
31939     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31940     {
31941         var pos = [];
31942         
31943         if(box[0].size == 'md-left'){
31944             pos.push({
31945                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31946                 y : minY
31947             });
31948             
31949             return pos;
31950         }
31951         
31952         if(box[0].size == 'md-right'){
31953             pos.push({
31954                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31955                 y : minY + (this.unitWidth + this.gutter) * 1
31956             });
31957             
31958             return pos;
31959         }
31960         
31961         var rand = Math.floor(Math.random() * (4 - box[0].y));
31962         
31963         pos.push({
31964             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31965             y : minY + (this.unitWidth + this.gutter) * rand
31966         });
31967         
31968         return pos;
31969         
31970     },
31971     
31972     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31973     {
31974         var pos = [];
31975         
31976         if(box[0].size == 'xs'){
31977             
31978             pos.push({
31979                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31980                 y : minY
31981             });
31982
31983             pos.push({
31984                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31985                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31986             });
31987             
31988             return pos;
31989             
31990         }
31991         
31992         pos.push({
31993             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31994             y : minY
31995         });
31996
31997         pos.push({
31998             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31999             y : minY + (this.unitWidth + this.gutter) * 2
32000         });
32001         
32002         return pos;
32003         
32004     },
32005     
32006     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32007     {
32008         var pos = [];
32009         
32010         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32011             
32012             pos.push({
32013                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32014                 y : minY
32015             });
32016
32017             pos.push({
32018                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32019                 y : minY + (this.unitWidth + this.gutter) * 1
32020             });
32021             
32022             pos.push({
32023                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32024                 y : minY + (this.unitWidth + this.gutter) * 2
32025             });
32026             
32027             return pos;
32028             
32029         }
32030         
32031         if(box[0].size == 'xs' && box[1].size == 'xs'){
32032             
32033             pos.push({
32034                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32035                 y : minY
32036             });
32037
32038             pos.push({
32039                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32040                 y : minY
32041             });
32042             
32043             pos.push({
32044                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32045                 y : minY + (this.unitWidth + this.gutter) * 1
32046             });
32047             
32048             return pos;
32049             
32050         }
32051         
32052         pos.push({
32053             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32054             y : minY
32055         });
32056
32057         pos.push({
32058             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32059             y : minY + (this.unitWidth + this.gutter) * 2
32060         });
32061
32062         pos.push({
32063             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32064             y : minY + (this.unitWidth + this.gutter) * 2
32065         });
32066             
32067         return pos;
32068         
32069     },
32070     
32071     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32072     {
32073         var pos = [];
32074         
32075         if(box[0].size == 'xs'){
32076             
32077             pos.push({
32078                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32079                 y : minY
32080             });
32081
32082             pos.push({
32083                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32084                 y : minY
32085             });
32086             
32087             pos.push({
32088                 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),
32089                 y : minY
32090             });
32091             
32092             pos.push({
32093                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32094                 y : minY + (this.unitWidth + this.gutter) * 1
32095             });
32096             
32097             return pos;
32098             
32099         }
32100         
32101         pos.push({
32102             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32103             y : minY
32104         });
32105         
32106         pos.push({
32107             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32108             y : minY + (this.unitWidth + this.gutter) * 2
32109         });
32110         
32111         pos.push({
32112             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32113             y : minY + (this.unitWidth + this.gutter) * 2
32114         });
32115         
32116         pos.push({
32117             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),
32118             y : minY + (this.unitWidth + this.gutter) * 2
32119         });
32120
32121         return pos;
32122         
32123     },
32124     
32125     /**
32126     * remove a Masonry Brick
32127     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32128     */
32129     removeBrick : function(brick_id)
32130     {
32131         if (!brick_id) {
32132             return;
32133         }
32134         
32135         for (var i = 0; i<this.bricks.length; i++) {
32136             if (this.bricks[i].id == brick_id) {
32137                 this.bricks.splice(i,1);
32138                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32139                 this.initial();
32140             }
32141         }
32142     },
32143     
32144     /**
32145     * adds a Masonry Brick
32146     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32147     */
32148     addBrick : function(cfg)
32149     {
32150         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32151         //this.register(cn);
32152         cn.parentId = this.id;
32153         cn.render(this.el);
32154         return cn;
32155     },
32156     
32157     /**
32158     * register a Masonry Brick
32159     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32160     */
32161     
32162     register : function(brick)
32163     {
32164         this.bricks.push(brick);
32165         brick.masonryId = this.id;
32166     },
32167     
32168     /**
32169     * clear all the Masonry Brick
32170     */
32171     clearAll : function()
32172     {
32173         this.bricks = [];
32174         //this.getChildContainer().dom.innerHTML = "";
32175         this.el.dom.innerHTML = '';
32176     },
32177     
32178     getSelected : function()
32179     {
32180         if (!this.selectedBrick) {
32181             return false;
32182         }
32183         
32184         return this.selectedBrick;
32185     }
32186 });
32187
32188 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32189     
32190     groups: {},
32191      /**
32192     * register a Masonry Layout
32193     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32194     */
32195     
32196     register : function(layout)
32197     {
32198         this.groups[layout.id] = layout;
32199     },
32200     /**
32201     * fetch a  Masonry Layout based on the masonry layout ID
32202     * @param {string} the masonry layout to add
32203     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32204     */
32205     
32206     get: function(layout_id) {
32207         if (typeof(this.groups[layout_id]) == 'undefined') {
32208             return false;
32209         }
32210         return this.groups[layout_id] ;
32211     }
32212     
32213     
32214     
32215 });
32216
32217  
32218
32219  /**
32220  *
32221  * This is based on 
32222  * http://masonry.desandro.com
32223  *
32224  * The idea is to render all the bricks based on vertical width...
32225  *
32226  * The original code extends 'outlayer' - we might need to use that....
32227  * 
32228  */
32229
32230
32231 /**
32232  * @class Roo.bootstrap.LayoutMasonryAuto
32233  * @extends Roo.bootstrap.Component
32234  * Bootstrap Layout Masonry class
32235  * 
32236  * @constructor
32237  * Create a new Element
32238  * @param {Object} config The config object
32239  */
32240
32241 Roo.bootstrap.LayoutMasonryAuto = function(config){
32242     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32243 };
32244
32245 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32246     
32247       /**
32248      * @cfg {Boolean} isFitWidth  - resize the width..
32249      */   
32250     isFitWidth : false,  // options..
32251     /**
32252      * @cfg {Boolean} isOriginLeft = left align?
32253      */   
32254     isOriginLeft : true,
32255     /**
32256      * @cfg {Boolean} isOriginTop = top align?
32257      */   
32258     isOriginTop : false,
32259     /**
32260      * @cfg {Boolean} isLayoutInstant = no animation?
32261      */   
32262     isLayoutInstant : false, // needed?
32263     /**
32264      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32265      */   
32266     isResizingContainer : true,
32267     /**
32268      * @cfg {Number} columnWidth  width of the columns 
32269      */   
32270     
32271     columnWidth : 0,
32272     
32273     /**
32274      * @cfg {Number} maxCols maximum number of columns
32275      */   
32276     
32277     maxCols: 0,
32278     /**
32279      * @cfg {Number} padHeight padding below box..
32280      */   
32281     
32282     padHeight : 10, 
32283     
32284     /**
32285      * @cfg {Boolean} isAutoInitial defalut true
32286      */   
32287     
32288     isAutoInitial : true, 
32289     
32290     // private?
32291     gutter : 0,
32292     
32293     containerWidth: 0,
32294     initialColumnWidth : 0,
32295     currentSize : null,
32296     
32297     colYs : null, // array.
32298     maxY : 0,
32299     padWidth: 10,
32300     
32301     
32302     tag: 'div',
32303     cls: '',
32304     bricks: null, //CompositeElement
32305     cols : 0, // array?
32306     // element : null, // wrapped now this.el
32307     _isLayoutInited : null, 
32308     
32309     
32310     getAutoCreate : function(){
32311         
32312         var cfg = {
32313             tag: this.tag,
32314             cls: 'blog-masonary-wrapper ' + this.cls,
32315             cn : {
32316                 cls : 'mas-boxes masonary'
32317             }
32318         };
32319         
32320         return cfg;
32321     },
32322     
32323     getChildContainer: function( )
32324     {
32325         if (this.boxesEl) {
32326             return this.boxesEl;
32327         }
32328         
32329         this.boxesEl = this.el.select('.mas-boxes').first();
32330         
32331         return this.boxesEl;
32332     },
32333     
32334     
32335     initEvents : function()
32336     {
32337         var _this = this;
32338         
32339         if(this.isAutoInitial){
32340             Roo.log('hook children rendered');
32341             this.on('childrenrendered', function() {
32342                 Roo.log('children rendered');
32343                 _this.initial();
32344             } ,this);
32345         }
32346         
32347     },
32348     
32349     initial : function()
32350     {
32351         this.reloadItems();
32352
32353         this.currentSize = this.el.getBox(true);
32354
32355         /// was window resize... - let's see if this works..
32356         Roo.EventManager.onWindowResize(this.resize, this); 
32357
32358         if(!this.isAutoInitial){
32359             this.layout();
32360             return;
32361         }
32362         
32363         this.layout.defer(500,this);
32364     },
32365     
32366     reloadItems: function()
32367     {
32368         this.bricks = this.el.select('.masonry-brick', true);
32369         
32370         this.bricks.each(function(b) {
32371             //Roo.log(b.getSize());
32372             if (!b.attr('originalwidth')) {
32373                 b.attr('originalwidth',  b.getSize().width);
32374             }
32375             
32376         });
32377         
32378         Roo.log(this.bricks.elements.length);
32379     },
32380     
32381     resize : function()
32382     {
32383         Roo.log('resize');
32384         var cs = this.el.getBox(true);
32385         
32386         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32387             Roo.log("no change in with or X");
32388             return;
32389         }
32390         this.currentSize = cs;
32391         this.layout();
32392     },
32393     
32394     layout : function()
32395     {
32396          Roo.log('layout');
32397         this._resetLayout();
32398         //this._manageStamps();
32399       
32400         // don't animate first layout
32401         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32402         this.layoutItems( isInstant );
32403       
32404         // flag for initalized
32405         this._isLayoutInited = true;
32406     },
32407     
32408     layoutItems : function( isInstant )
32409     {
32410         //var items = this._getItemsForLayout( this.items );
32411         // original code supports filtering layout items.. we just ignore it..
32412         
32413         this._layoutItems( this.bricks , isInstant );
32414       
32415         this._postLayout();
32416     },
32417     _layoutItems : function ( items , isInstant)
32418     {
32419        //this.fireEvent( 'layout', this, items );
32420     
32421
32422         if ( !items || !items.elements.length ) {
32423           // no items, emit event with empty array
32424             return;
32425         }
32426
32427         var queue = [];
32428         items.each(function(item) {
32429             Roo.log("layout item");
32430             Roo.log(item);
32431             // get x/y object from method
32432             var position = this._getItemLayoutPosition( item );
32433             // enqueue
32434             position.item = item;
32435             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32436             queue.push( position );
32437         }, this);
32438       
32439         this._processLayoutQueue( queue );
32440     },
32441     /** Sets position of item in DOM
32442     * @param {Element} item
32443     * @param {Number} x - horizontal position
32444     * @param {Number} y - vertical position
32445     * @param {Boolean} isInstant - disables transitions
32446     */
32447     _processLayoutQueue : function( queue )
32448     {
32449         for ( var i=0, len = queue.length; i < len; i++ ) {
32450             var obj = queue[i];
32451             obj.item.position('absolute');
32452             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32453         }
32454     },
32455       
32456     
32457     /**
32458     * Any logic you want to do after each layout,
32459     * i.e. size the container
32460     */
32461     _postLayout : function()
32462     {
32463         this.resizeContainer();
32464     },
32465     
32466     resizeContainer : function()
32467     {
32468         if ( !this.isResizingContainer ) {
32469             return;
32470         }
32471         var size = this._getContainerSize();
32472         if ( size ) {
32473             this.el.setSize(size.width,size.height);
32474             this.boxesEl.setSize(size.width,size.height);
32475         }
32476     },
32477     
32478     
32479     
32480     _resetLayout : function()
32481     {
32482         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32483         this.colWidth = this.el.getWidth();
32484         //this.gutter = this.el.getWidth(); 
32485         
32486         this.measureColumns();
32487
32488         // reset column Y
32489         var i = this.cols;
32490         this.colYs = [];
32491         while (i--) {
32492             this.colYs.push( 0 );
32493         }
32494     
32495         this.maxY = 0;
32496     },
32497
32498     measureColumns : function()
32499     {
32500         this.getContainerWidth();
32501       // if columnWidth is 0, default to outerWidth of first item
32502         if ( !this.columnWidth ) {
32503             var firstItem = this.bricks.first();
32504             Roo.log(firstItem);
32505             this.columnWidth  = this.containerWidth;
32506             if (firstItem && firstItem.attr('originalwidth') ) {
32507                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32508             }
32509             // columnWidth fall back to item of first element
32510             Roo.log("set column width?");
32511                         this.initialColumnWidth = this.columnWidth  ;
32512
32513             // if first elem has no width, default to size of container
32514             
32515         }
32516         
32517         
32518         if (this.initialColumnWidth) {
32519             this.columnWidth = this.initialColumnWidth;
32520         }
32521         
32522         
32523             
32524         // column width is fixed at the top - however if container width get's smaller we should
32525         // reduce it...
32526         
32527         // this bit calcs how man columns..
32528             
32529         var columnWidth = this.columnWidth += this.gutter;
32530       
32531         // calculate columns
32532         var containerWidth = this.containerWidth + this.gutter;
32533         
32534         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32535         // fix rounding errors, typically with gutters
32536         var excess = columnWidth - containerWidth % columnWidth;
32537         
32538         
32539         // if overshoot is less than a pixel, round up, otherwise floor it
32540         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32541         cols = Math[ mathMethod ]( cols );
32542         this.cols = Math.max( cols, 1 );
32543         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32544         
32545          // padding positioning..
32546         var totalColWidth = this.cols * this.columnWidth;
32547         var padavail = this.containerWidth - totalColWidth;
32548         // so for 2 columns - we need 3 'pads'
32549         
32550         var padNeeded = (1+this.cols) * this.padWidth;
32551         
32552         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32553         
32554         this.columnWidth += padExtra
32555         //this.padWidth = Math.floor(padavail /  ( this.cols));
32556         
32557         // adjust colum width so that padding is fixed??
32558         
32559         // we have 3 columns ... total = width * 3
32560         // we have X left over... that should be used by 
32561         
32562         //if (this.expandC) {
32563             
32564         //}
32565         
32566         
32567         
32568     },
32569     
32570     getContainerWidth : function()
32571     {
32572        /* // container is parent if fit width
32573         var container = this.isFitWidth ? this.element.parentNode : this.element;
32574         // check that this.size and size are there
32575         // IE8 triggers resize on body size change, so they might not be
32576         
32577         var size = getSize( container );  //FIXME
32578         this.containerWidth = size && size.innerWidth; //FIXME
32579         */
32580          
32581         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32582         
32583     },
32584     
32585     _getItemLayoutPosition : function( item )  // what is item?
32586     {
32587         // we resize the item to our columnWidth..
32588       
32589         item.setWidth(this.columnWidth);
32590         item.autoBoxAdjust  = false;
32591         
32592         var sz = item.getSize();
32593  
32594         // how many columns does this brick span
32595         var remainder = this.containerWidth % this.columnWidth;
32596         
32597         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32598         // round if off by 1 pixel, otherwise use ceil
32599         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32600         colSpan = Math.min( colSpan, this.cols );
32601         
32602         // normally this should be '1' as we dont' currently allow multi width columns..
32603         
32604         var colGroup = this._getColGroup( colSpan );
32605         // get the minimum Y value from the columns
32606         var minimumY = Math.min.apply( Math, colGroup );
32607         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32608         
32609         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32610          
32611         // position the brick
32612         var position = {
32613             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32614             y: this.currentSize.y + minimumY + this.padHeight
32615         };
32616         
32617         Roo.log(position);
32618         // apply setHeight to necessary columns
32619         var setHeight = minimumY + sz.height + this.padHeight;
32620         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32621         
32622         var setSpan = this.cols + 1 - colGroup.length;
32623         for ( var i = 0; i < setSpan; i++ ) {
32624           this.colYs[ shortColIndex + i ] = setHeight ;
32625         }
32626       
32627         return position;
32628     },
32629     
32630     /**
32631      * @param {Number} colSpan - number of columns the element spans
32632      * @returns {Array} colGroup
32633      */
32634     _getColGroup : function( colSpan )
32635     {
32636         if ( colSpan < 2 ) {
32637           // if brick spans only one column, use all the column Ys
32638           return this.colYs;
32639         }
32640       
32641         var colGroup = [];
32642         // how many different places could this brick fit horizontally
32643         var groupCount = this.cols + 1 - colSpan;
32644         // for each group potential horizontal position
32645         for ( var i = 0; i < groupCount; i++ ) {
32646           // make an array of colY values for that one group
32647           var groupColYs = this.colYs.slice( i, i + colSpan );
32648           // and get the max value of the array
32649           colGroup[i] = Math.max.apply( Math, groupColYs );
32650         }
32651         return colGroup;
32652     },
32653     /*
32654     _manageStamp : function( stamp )
32655     {
32656         var stampSize =  stamp.getSize();
32657         var offset = stamp.getBox();
32658         // get the columns that this stamp affects
32659         var firstX = this.isOriginLeft ? offset.x : offset.right;
32660         var lastX = firstX + stampSize.width;
32661         var firstCol = Math.floor( firstX / this.columnWidth );
32662         firstCol = Math.max( 0, firstCol );
32663         
32664         var lastCol = Math.floor( lastX / this.columnWidth );
32665         // lastCol should not go over if multiple of columnWidth #425
32666         lastCol -= lastX % this.columnWidth ? 0 : 1;
32667         lastCol = Math.min( this.cols - 1, lastCol );
32668         
32669         // set colYs to bottom of the stamp
32670         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32671             stampSize.height;
32672             
32673         for ( var i = firstCol; i <= lastCol; i++ ) {
32674           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32675         }
32676     },
32677     */
32678     
32679     _getContainerSize : function()
32680     {
32681         this.maxY = Math.max.apply( Math, this.colYs );
32682         var size = {
32683             height: this.maxY
32684         };
32685       
32686         if ( this.isFitWidth ) {
32687             size.width = this._getContainerFitWidth();
32688         }
32689       
32690         return size;
32691     },
32692     
32693     _getContainerFitWidth : function()
32694     {
32695         var unusedCols = 0;
32696         // count unused columns
32697         var i = this.cols;
32698         while ( --i ) {
32699           if ( this.colYs[i] !== 0 ) {
32700             break;
32701           }
32702           unusedCols++;
32703         }
32704         // fit container to columns that have been used
32705         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32706     },
32707     
32708     needsResizeLayout : function()
32709     {
32710         var previousWidth = this.containerWidth;
32711         this.getContainerWidth();
32712         return previousWidth !== this.containerWidth;
32713     }
32714  
32715 });
32716
32717  
32718
32719  /*
32720  * - LGPL
32721  *
32722  * element
32723  * 
32724  */
32725
32726 /**
32727  * @class Roo.bootstrap.MasonryBrick
32728  * @extends Roo.bootstrap.Component
32729  * Bootstrap MasonryBrick class
32730  * 
32731  * @constructor
32732  * Create a new MasonryBrick
32733  * @param {Object} config The config object
32734  */
32735
32736 Roo.bootstrap.MasonryBrick = function(config){
32737     
32738     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32739     
32740     Roo.bootstrap.MasonryBrick.register(this);
32741     
32742     this.addEvents({
32743         // raw events
32744         /**
32745          * @event click
32746          * When a MasonryBrick is clcik
32747          * @param {Roo.bootstrap.MasonryBrick} this
32748          * @param {Roo.EventObject} e
32749          */
32750         "click" : true
32751     });
32752 };
32753
32754 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32755     
32756     /**
32757      * @cfg {String} title
32758      */   
32759     title : '',
32760     /**
32761      * @cfg {String} html
32762      */   
32763     html : '',
32764     /**
32765      * @cfg {String} bgimage
32766      */   
32767     bgimage : '',
32768     /**
32769      * @cfg {String} videourl
32770      */   
32771     videourl : '',
32772     /**
32773      * @cfg {String} cls
32774      */   
32775     cls : '',
32776     /**
32777      * @cfg {String} href
32778      */   
32779     href : '',
32780     /**
32781      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32782      */   
32783     size : 'xs',
32784     
32785     /**
32786      * @cfg {String} placetitle (center|bottom)
32787      */   
32788     placetitle : '',
32789     
32790     /**
32791      * @cfg {Boolean} isFitContainer defalut true
32792      */   
32793     isFitContainer : true, 
32794     
32795     /**
32796      * @cfg {Boolean} preventDefault defalut false
32797      */   
32798     preventDefault : false, 
32799     
32800     /**
32801      * @cfg {Boolean} inverse defalut false
32802      */   
32803     maskInverse : false, 
32804     
32805     getAutoCreate : function()
32806     {
32807         if(!this.isFitContainer){
32808             return this.getSplitAutoCreate();
32809         }
32810         
32811         var cls = 'masonry-brick masonry-brick-full';
32812         
32813         if(this.href.length){
32814             cls += ' masonry-brick-link';
32815         }
32816         
32817         if(this.bgimage.length){
32818             cls += ' masonry-brick-image';
32819         }
32820         
32821         if(this.maskInverse){
32822             cls += ' mask-inverse';
32823         }
32824         
32825         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32826             cls += ' enable-mask';
32827         }
32828         
32829         if(this.size){
32830             cls += ' masonry-' + this.size + '-brick';
32831         }
32832         
32833         if(this.placetitle.length){
32834             
32835             switch (this.placetitle) {
32836                 case 'center' :
32837                     cls += ' masonry-center-title';
32838                     break;
32839                 case 'bottom' :
32840                     cls += ' masonry-bottom-title';
32841                     break;
32842                 default:
32843                     break;
32844             }
32845             
32846         } else {
32847             if(!this.html.length && !this.bgimage.length){
32848                 cls += ' masonry-center-title';
32849             }
32850
32851             if(!this.html.length && this.bgimage.length){
32852                 cls += ' masonry-bottom-title';
32853             }
32854         }
32855         
32856         if(this.cls){
32857             cls += ' ' + this.cls;
32858         }
32859         
32860         var cfg = {
32861             tag: (this.href.length) ? 'a' : 'div',
32862             cls: cls,
32863             cn: [
32864                 {
32865                     tag: 'div',
32866                     cls: 'masonry-brick-mask'
32867                 },
32868                 {
32869                     tag: 'div',
32870                     cls: 'masonry-brick-paragraph',
32871                     cn: []
32872                 }
32873             ]
32874         };
32875         
32876         if(this.href.length){
32877             cfg.href = this.href;
32878         }
32879         
32880         var cn = cfg.cn[1].cn;
32881         
32882         if(this.title.length){
32883             cn.push({
32884                 tag: 'h4',
32885                 cls: 'masonry-brick-title',
32886                 html: this.title
32887             });
32888         }
32889         
32890         if(this.html.length){
32891             cn.push({
32892                 tag: 'p',
32893                 cls: 'masonry-brick-text',
32894                 html: this.html
32895             });
32896         }
32897         
32898         if (!this.title.length && !this.html.length) {
32899             cfg.cn[1].cls += ' hide';
32900         }
32901         
32902         if(this.bgimage.length){
32903             cfg.cn.push({
32904                 tag: 'img',
32905                 cls: 'masonry-brick-image-view',
32906                 src: this.bgimage
32907             });
32908         }
32909         
32910         if(this.videourl.length){
32911             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32912             // youtube support only?
32913             cfg.cn.push({
32914                 tag: 'iframe',
32915                 cls: 'masonry-brick-image-view',
32916                 src: vurl,
32917                 frameborder : 0,
32918                 allowfullscreen : true
32919             });
32920         }
32921         
32922         return cfg;
32923         
32924     },
32925     
32926     getSplitAutoCreate : function()
32927     {
32928         var cls = 'masonry-brick masonry-brick-split';
32929         
32930         if(this.href.length){
32931             cls += ' masonry-brick-link';
32932         }
32933         
32934         if(this.bgimage.length){
32935             cls += ' masonry-brick-image';
32936         }
32937         
32938         if(this.size){
32939             cls += ' masonry-' + this.size + '-brick';
32940         }
32941         
32942         switch (this.placetitle) {
32943             case 'center' :
32944                 cls += ' masonry-center-title';
32945                 break;
32946             case 'bottom' :
32947                 cls += ' masonry-bottom-title';
32948                 break;
32949             default:
32950                 if(!this.bgimage.length){
32951                     cls += ' masonry-center-title';
32952                 }
32953
32954                 if(this.bgimage.length){
32955                     cls += ' masonry-bottom-title';
32956                 }
32957                 break;
32958         }
32959         
32960         if(this.cls){
32961             cls += ' ' + this.cls;
32962         }
32963         
32964         var cfg = {
32965             tag: (this.href.length) ? 'a' : 'div',
32966             cls: cls,
32967             cn: [
32968                 {
32969                     tag: 'div',
32970                     cls: 'masonry-brick-split-head',
32971                     cn: [
32972                         {
32973                             tag: 'div',
32974                             cls: 'masonry-brick-paragraph',
32975                             cn: []
32976                         }
32977                     ]
32978                 },
32979                 {
32980                     tag: 'div',
32981                     cls: 'masonry-brick-split-body',
32982                     cn: []
32983                 }
32984             ]
32985         };
32986         
32987         if(this.href.length){
32988             cfg.href = this.href;
32989         }
32990         
32991         if(this.title.length){
32992             cfg.cn[0].cn[0].cn.push({
32993                 tag: 'h4',
32994                 cls: 'masonry-brick-title',
32995                 html: this.title
32996             });
32997         }
32998         
32999         if(this.html.length){
33000             cfg.cn[1].cn.push({
33001                 tag: 'p',
33002                 cls: 'masonry-brick-text',
33003                 html: this.html
33004             });
33005         }
33006
33007         if(this.bgimage.length){
33008             cfg.cn[0].cn.push({
33009                 tag: 'img',
33010                 cls: 'masonry-brick-image-view',
33011                 src: this.bgimage
33012             });
33013         }
33014         
33015         if(this.videourl.length){
33016             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33017             // youtube support only?
33018             cfg.cn[0].cn.cn.push({
33019                 tag: 'iframe',
33020                 cls: 'masonry-brick-image-view',
33021                 src: vurl,
33022                 frameborder : 0,
33023                 allowfullscreen : true
33024             });
33025         }
33026         
33027         return cfg;
33028     },
33029     
33030     initEvents: function() 
33031     {
33032         switch (this.size) {
33033             case 'xs' :
33034                 this.x = 1;
33035                 this.y = 1;
33036                 break;
33037             case 'sm' :
33038                 this.x = 2;
33039                 this.y = 2;
33040                 break;
33041             case 'md' :
33042             case 'md-left' :
33043             case 'md-right' :
33044                 this.x = 3;
33045                 this.y = 3;
33046                 break;
33047             case 'tall' :
33048                 this.x = 2;
33049                 this.y = 3;
33050                 break;
33051             case 'wide' :
33052                 this.x = 3;
33053                 this.y = 2;
33054                 break;
33055             case 'wide-thin' :
33056                 this.x = 3;
33057                 this.y = 1;
33058                 break;
33059                         
33060             default :
33061                 break;
33062         }
33063         
33064         if(Roo.isTouch){
33065             this.el.on('touchstart', this.onTouchStart, this);
33066             this.el.on('touchmove', this.onTouchMove, this);
33067             this.el.on('touchend', this.onTouchEnd, this);
33068             this.el.on('contextmenu', this.onContextMenu, this);
33069         } else {
33070             this.el.on('mouseenter'  ,this.enter, this);
33071             this.el.on('mouseleave', this.leave, this);
33072             this.el.on('click', this.onClick, this);
33073         }
33074         
33075         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33076             this.parent().bricks.push(this);   
33077         }
33078         
33079     },
33080     
33081     onClick: function(e, el)
33082     {
33083         var time = this.endTimer - this.startTimer;
33084         // Roo.log(e.preventDefault());
33085         if(Roo.isTouch){
33086             if(time > 1000){
33087                 e.preventDefault();
33088                 return;
33089             }
33090         }
33091         
33092         if(!this.preventDefault){
33093             return;
33094         }
33095         
33096         e.preventDefault();
33097         
33098         if (this.activeClass != '') {
33099             this.selectBrick();
33100         }
33101         
33102         this.fireEvent('click', this, e);
33103     },
33104     
33105     enter: function(e, el)
33106     {
33107         e.preventDefault();
33108         
33109         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33110             return;
33111         }
33112         
33113         if(this.bgimage.length && this.html.length){
33114             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33115         }
33116     },
33117     
33118     leave: function(e, el)
33119     {
33120         e.preventDefault();
33121         
33122         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33123             return;
33124         }
33125         
33126         if(this.bgimage.length && this.html.length){
33127             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33128         }
33129     },
33130     
33131     onTouchStart: function(e, el)
33132     {
33133 //        e.preventDefault();
33134         
33135         this.touchmoved = false;
33136         
33137         if(!this.isFitContainer){
33138             return;
33139         }
33140         
33141         if(!this.bgimage.length || !this.html.length){
33142             return;
33143         }
33144         
33145         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33146         
33147         this.timer = new Date().getTime();
33148         
33149     },
33150     
33151     onTouchMove: function(e, el)
33152     {
33153         this.touchmoved = true;
33154     },
33155     
33156     onContextMenu : function(e,el)
33157     {
33158         e.preventDefault();
33159         e.stopPropagation();
33160         return false;
33161     },
33162     
33163     onTouchEnd: function(e, el)
33164     {
33165 //        e.preventDefault();
33166         
33167         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33168         
33169             this.leave(e,el);
33170             
33171             return;
33172         }
33173         
33174         if(!this.bgimage.length || !this.html.length){
33175             
33176             if(this.href.length){
33177                 window.location.href = this.href;
33178             }
33179             
33180             return;
33181         }
33182         
33183         if(!this.isFitContainer){
33184             return;
33185         }
33186         
33187         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33188         
33189         window.location.href = this.href;
33190     },
33191     
33192     //selection on single brick only
33193     selectBrick : function() {
33194         
33195         if (!this.parentId) {
33196             return;
33197         }
33198         
33199         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33200         var index = m.selectedBrick.indexOf(this.id);
33201         
33202         if ( index > -1) {
33203             m.selectedBrick.splice(index,1);
33204             this.el.removeClass(this.activeClass);
33205             return;
33206         }
33207         
33208         for(var i = 0; i < m.selectedBrick.length; i++) {
33209             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33210             b.el.removeClass(b.activeClass);
33211         }
33212         
33213         m.selectedBrick = [];
33214         
33215         m.selectedBrick.push(this.id);
33216         this.el.addClass(this.activeClass);
33217         return;
33218     },
33219     
33220     isSelected : function(){
33221         return this.el.hasClass(this.activeClass);
33222         
33223     }
33224 });
33225
33226 Roo.apply(Roo.bootstrap.MasonryBrick, {
33227     
33228     //groups: {},
33229     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33230      /**
33231     * register a Masonry Brick
33232     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33233     */
33234     
33235     register : function(brick)
33236     {
33237         //this.groups[brick.id] = brick;
33238         this.groups.add(brick.id, brick);
33239     },
33240     /**
33241     * fetch a  masonry brick based on the masonry brick ID
33242     * @param {string} the masonry brick to add
33243     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33244     */
33245     
33246     get: function(brick_id) 
33247     {
33248         // if (typeof(this.groups[brick_id]) == 'undefined') {
33249         //     return false;
33250         // }
33251         // return this.groups[brick_id] ;
33252         
33253         if(this.groups.key(brick_id)) {
33254             return this.groups.key(brick_id);
33255         }
33256         
33257         return false;
33258     }
33259     
33260     
33261     
33262 });
33263
33264  /*
33265  * - LGPL
33266  *
33267  * element
33268  * 
33269  */
33270
33271 /**
33272  * @class Roo.bootstrap.Brick
33273  * @extends Roo.bootstrap.Component
33274  * Bootstrap Brick class
33275  * 
33276  * @constructor
33277  * Create a new Brick
33278  * @param {Object} config The config object
33279  */
33280
33281 Roo.bootstrap.Brick = function(config){
33282     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33283     
33284     this.addEvents({
33285         // raw events
33286         /**
33287          * @event click
33288          * When a Brick is click
33289          * @param {Roo.bootstrap.Brick} this
33290          * @param {Roo.EventObject} e
33291          */
33292         "click" : true
33293     });
33294 };
33295
33296 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33297     
33298     /**
33299      * @cfg {String} title
33300      */   
33301     title : '',
33302     /**
33303      * @cfg {String} html
33304      */   
33305     html : '',
33306     /**
33307      * @cfg {String} bgimage
33308      */   
33309     bgimage : '',
33310     /**
33311      * @cfg {String} cls
33312      */   
33313     cls : '',
33314     /**
33315      * @cfg {String} href
33316      */   
33317     href : '',
33318     /**
33319      * @cfg {String} video
33320      */   
33321     video : '',
33322     /**
33323      * @cfg {Boolean} square
33324      */   
33325     square : true,
33326     
33327     getAutoCreate : function()
33328     {
33329         var cls = 'roo-brick';
33330         
33331         if(this.href.length){
33332             cls += ' roo-brick-link';
33333         }
33334         
33335         if(this.bgimage.length){
33336             cls += ' roo-brick-image';
33337         }
33338         
33339         if(!this.html.length && !this.bgimage.length){
33340             cls += ' roo-brick-center-title';
33341         }
33342         
33343         if(!this.html.length && this.bgimage.length){
33344             cls += ' roo-brick-bottom-title';
33345         }
33346         
33347         if(this.cls){
33348             cls += ' ' + this.cls;
33349         }
33350         
33351         var cfg = {
33352             tag: (this.href.length) ? 'a' : 'div',
33353             cls: cls,
33354             cn: [
33355                 {
33356                     tag: 'div',
33357                     cls: 'roo-brick-paragraph',
33358                     cn: []
33359                 }
33360             ]
33361         };
33362         
33363         if(this.href.length){
33364             cfg.href = this.href;
33365         }
33366         
33367         var cn = cfg.cn[0].cn;
33368         
33369         if(this.title.length){
33370             cn.push({
33371                 tag: 'h4',
33372                 cls: 'roo-brick-title',
33373                 html: this.title
33374             });
33375         }
33376         
33377         if(this.html.length){
33378             cn.push({
33379                 tag: 'p',
33380                 cls: 'roo-brick-text',
33381                 html: this.html
33382             });
33383         } else {
33384             cn.cls += ' hide';
33385         }
33386         
33387         if(this.bgimage.length){
33388             cfg.cn.push({
33389                 tag: 'img',
33390                 cls: 'roo-brick-image-view',
33391                 src: this.bgimage
33392             });
33393         }
33394         
33395         return cfg;
33396     },
33397     
33398     initEvents: function() 
33399     {
33400         if(this.title.length || this.html.length){
33401             this.el.on('mouseenter'  ,this.enter, this);
33402             this.el.on('mouseleave', this.leave, this);
33403         }
33404         
33405         Roo.EventManager.onWindowResize(this.resize, this); 
33406         
33407         if(this.bgimage.length){
33408             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33409             this.imageEl.on('load', this.onImageLoad, this);
33410             return;
33411         }
33412         
33413         this.resize();
33414     },
33415     
33416     onImageLoad : function()
33417     {
33418         this.resize();
33419     },
33420     
33421     resize : function()
33422     {
33423         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33424         
33425         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33426         
33427         if(this.bgimage.length){
33428             var image = this.el.select('.roo-brick-image-view', true).first();
33429             
33430             image.setWidth(paragraph.getWidth());
33431             
33432             if(this.square){
33433                 image.setHeight(paragraph.getWidth());
33434             }
33435             
33436             this.el.setHeight(image.getHeight());
33437             paragraph.setHeight(image.getHeight());
33438             
33439         }
33440         
33441     },
33442     
33443     enter: function(e, el)
33444     {
33445         e.preventDefault();
33446         
33447         if(this.bgimage.length){
33448             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33449             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33450         }
33451     },
33452     
33453     leave: function(e, el)
33454     {
33455         e.preventDefault();
33456         
33457         if(this.bgimage.length){
33458             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33459             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33460         }
33461     }
33462     
33463 });
33464
33465  
33466
33467  /*
33468  * - LGPL
33469  *
33470  * Number field 
33471  */
33472
33473 /**
33474  * @class Roo.bootstrap.NumberField
33475  * @extends Roo.bootstrap.Input
33476  * Bootstrap NumberField class
33477  * 
33478  * 
33479  * 
33480  * 
33481  * @constructor
33482  * Create a new NumberField
33483  * @param {Object} config The config object
33484  */
33485
33486 Roo.bootstrap.NumberField = function(config){
33487     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33488 };
33489
33490 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33491     
33492     /**
33493      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33494      */
33495     allowDecimals : true,
33496     /**
33497      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33498      */
33499     decimalSeparator : ".",
33500     /**
33501      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33502      */
33503     decimalPrecision : 2,
33504     /**
33505      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33506      */
33507     allowNegative : true,
33508     
33509     /**
33510      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33511      */
33512     allowZero: true,
33513     /**
33514      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33515      */
33516     minValue : Number.NEGATIVE_INFINITY,
33517     /**
33518      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33519      */
33520     maxValue : Number.MAX_VALUE,
33521     /**
33522      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33523      */
33524     minText : "The minimum value for this field is {0}",
33525     /**
33526      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33527      */
33528     maxText : "The maximum value for this field is {0}",
33529     /**
33530      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33531      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33532      */
33533     nanText : "{0} is not a valid number",
33534     /**
33535      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33536      */
33537     thousandsDelimiter : false,
33538     /**
33539      * @cfg {String} valueAlign alignment of value
33540      */
33541     valueAlign : "left",
33542
33543     getAutoCreate : function()
33544     {
33545         var hiddenInput = {
33546             tag: 'input',
33547             type: 'hidden',
33548             id: Roo.id(),
33549             cls: 'hidden-number-input'
33550         };
33551         
33552         if (this.name) {
33553             hiddenInput.name = this.name;
33554         }
33555         
33556         this.name = '';
33557         
33558         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33559         
33560         this.name = hiddenInput.name;
33561         
33562         if(cfg.cn.length > 0) {
33563             cfg.cn.push(hiddenInput);
33564         }
33565         
33566         return cfg;
33567     },
33568
33569     // private
33570     initEvents : function()
33571     {   
33572         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33573         
33574         var allowed = "0123456789";
33575         
33576         if(this.allowDecimals){
33577             allowed += this.decimalSeparator;
33578         }
33579         
33580         if(this.allowNegative){
33581             allowed += "-";
33582         }
33583         
33584         if(this.thousandsDelimiter) {
33585             allowed += ",";
33586         }
33587         
33588         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33589         
33590         var keyPress = function(e){
33591             
33592             var k = e.getKey();
33593             
33594             var c = e.getCharCode();
33595             
33596             if(
33597                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33598                     allowed.indexOf(String.fromCharCode(c)) === -1
33599             ){
33600                 e.stopEvent();
33601                 return;
33602             }
33603             
33604             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33605                 return;
33606             }
33607             
33608             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33609                 e.stopEvent();
33610             }
33611         };
33612         
33613         this.el.on("keypress", keyPress, this);
33614     },
33615     
33616     validateValue : function(value)
33617     {
33618         
33619         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33620             return false;
33621         }
33622         
33623         var num = this.parseValue(value);
33624         
33625         if(isNaN(num)){
33626             this.markInvalid(String.format(this.nanText, value));
33627             return false;
33628         }
33629         
33630         if(num < this.minValue){
33631             this.markInvalid(String.format(this.minText, this.minValue));
33632             return false;
33633         }
33634         
33635         if(num > this.maxValue){
33636             this.markInvalid(String.format(this.maxText, this.maxValue));
33637             return false;
33638         }
33639         
33640         return true;
33641     },
33642
33643     getValue : function()
33644     {
33645         var v = this.hiddenEl().getValue();
33646         
33647         return this.fixPrecision(this.parseValue(v));
33648     },
33649
33650     parseValue : function(value)
33651     {
33652         if(this.thousandsDelimiter) {
33653             value += "";
33654             r = new RegExp(",", "g");
33655             value = value.replace(r, "");
33656         }
33657         
33658         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33659         return isNaN(value) ? '' : value;
33660     },
33661
33662     fixPrecision : function(value)
33663     {
33664         if(this.thousandsDelimiter) {
33665             value += "";
33666             r = new RegExp(",", "g");
33667             value = value.replace(r, "");
33668         }
33669         
33670         var nan = isNaN(value);
33671         
33672         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33673             return nan ? '' : value;
33674         }
33675         return parseFloat(value).toFixed(this.decimalPrecision);
33676     },
33677
33678     setValue : function(v)
33679     {
33680         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33681         
33682         this.value = v;
33683         
33684         if(this.rendered){
33685             
33686             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33687             
33688             this.inputEl().dom.value = (v == '') ? '' :
33689                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33690             
33691             if(!this.allowZero && v === '0') {
33692                 this.hiddenEl().dom.value = '';
33693                 this.inputEl().dom.value = '';
33694             }
33695             
33696             this.validate();
33697         }
33698     },
33699
33700     decimalPrecisionFcn : function(v)
33701     {
33702         return Math.floor(v);
33703     },
33704
33705     beforeBlur : function()
33706     {
33707         var v = this.parseValue(this.getRawValue());
33708         
33709         if(v || v === 0 || v === ''){
33710             this.setValue(v);
33711         }
33712     },
33713     
33714     hiddenEl : function()
33715     {
33716         return this.el.select('input.hidden-number-input',true).first();
33717     }
33718     
33719 });
33720
33721  
33722
33723 /*
33724 * Licence: LGPL
33725 */
33726
33727 /**
33728  * @class Roo.bootstrap.DocumentSlider
33729  * @extends Roo.bootstrap.Component
33730  * Bootstrap DocumentSlider class
33731  * 
33732  * @constructor
33733  * Create a new DocumentViewer
33734  * @param {Object} config The config object
33735  */
33736
33737 Roo.bootstrap.DocumentSlider = function(config){
33738     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33739     
33740     this.files = [];
33741     
33742     this.addEvents({
33743         /**
33744          * @event initial
33745          * Fire after initEvent
33746          * @param {Roo.bootstrap.DocumentSlider} this
33747          */
33748         "initial" : true,
33749         /**
33750          * @event update
33751          * Fire after update
33752          * @param {Roo.bootstrap.DocumentSlider} this
33753          */
33754         "update" : true,
33755         /**
33756          * @event click
33757          * Fire after click
33758          * @param {Roo.bootstrap.DocumentSlider} this
33759          */
33760         "click" : true
33761     });
33762 };
33763
33764 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33765     
33766     files : false,
33767     
33768     indicator : 0,
33769     
33770     getAutoCreate : function()
33771     {
33772         var cfg = {
33773             tag : 'div',
33774             cls : 'roo-document-slider',
33775             cn : [
33776                 {
33777                     tag : 'div',
33778                     cls : 'roo-document-slider-header',
33779                     cn : [
33780                         {
33781                             tag : 'div',
33782                             cls : 'roo-document-slider-header-title'
33783                         }
33784                     ]
33785                 },
33786                 {
33787                     tag : 'div',
33788                     cls : 'roo-document-slider-body',
33789                     cn : [
33790                         {
33791                             tag : 'div',
33792                             cls : 'roo-document-slider-prev',
33793                             cn : [
33794                                 {
33795                                     tag : 'i',
33796                                     cls : 'fa fa-chevron-left'
33797                                 }
33798                             ]
33799                         },
33800                         {
33801                             tag : 'div',
33802                             cls : 'roo-document-slider-thumb',
33803                             cn : [
33804                                 {
33805                                     tag : 'img',
33806                                     cls : 'roo-document-slider-image'
33807                                 }
33808                             ]
33809                         },
33810                         {
33811                             tag : 'div',
33812                             cls : 'roo-document-slider-next',
33813                             cn : [
33814                                 {
33815                                     tag : 'i',
33816                                     cls : 'fa fa-chevron-right'
33817                                 }
33818                             ]
33819                         }
33820                     ]
33821                 }
33822             ]
33823         };
33824         
33825         return cfg;
33826     },
33827     
33828     initEvents : function()
33829     {
33830         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33831         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33832         
33833         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33834         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33835         
33836         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33837         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33838         
33839         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33840         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33841         
33842         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33843         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33844         
33845         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33846         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33847         
33848         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33849         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33850         
33851         this.thumbEl.on('click', this.onClick, this);
33852         
33853         this.prevIndicator.on('click', this.prev, this);
33854         
33855         this.nextIndicator.on('click', this.next, this);
33856         
33857     },
33858     
33859     initial : function()
33860     {
33861         if(this.files.length){
33862             this.indicator = 1;
33863             this.update()
33864         }
33865         
33866         this.fireEvent('initial', this);
33867     },
33868     
33869     update : function()
33870     {
33871         this.imageEl.attr('src', this.files[this.indicator - 1]);
33872         
33873         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33874         
33875         this.prevIndicator.show();
33876         
33877         if(this.indicator == 1){
33878             this.prevIndicator.hide();
33879         }
33880         
33881         this.nextIndicator.show();
33882         
33883         if(this.indicator == this.files.length){
33884             this.nextIndicator.hide();
33885         }
33886         
33887         this.thumbEl.scrollTo('top');
33888         
33889         this.fireEvent('update', this);
33890     },
33891     
33892     onClick : function(e)
33893     {
33894         e.preventDefault();
33895         
33896         this.fireEvent('click', this);
33897     },
33898     
33899     prev : function(e)
33900     {
33901         e.preventDefault();
33902         
33903         this.indicator = Math.max(1, this.indicator - 1);
33904         
33905         this.update();
33906     },
33907     
33908     next : function(e)
33909     {
33910         e.preventDefault();
33911         
33912         this.indicator = Math.min(this.files.length, this.indicator + 1);
33913         
33914         this.update();
33915     }
33916 });
33917 /*
33918  * - LGPL
33919  *
33920  * RadioSet
33921  *
33922  *
33923  */
33924
33925 /**
33926  * @class Roo.bootstrap.RadioSet
33927  * @extends Roo.bootstrap.Input
33928  * Bootstrap RadioSet class
33929  * @cfg {String} indicatorpos (left|right) default left
33930  * @cfg {Boolean} inline (true|false) inline the element (default true)
33931  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33932  * @constructor
33933  * Create a new RadioSet
33934  * @param {Object} config The config object
33935  */
33936
33937 Roo.bootstrap.RadioSet = function(config){
33938     
33939     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33940     
33941     this.radioes = [];
33942     
33943     Roo.bootstrap.RadioSet.register(this);
33944     
33945     this.addEvents({
33946         /**
33947         * @event check
33948         * Fires when the element is checked or unchecked.
33949         * @param {Roo.bootstrap.RadioSet} this This radio
33950         * @param {Roo.bootstrap.Radio} item The checked item
33951         */
33952        check : true,
33953        /**
33954         * @event click
33955         * Fires when the element is click.
33956         * @param {Roo.bootstrap.RadioSet} this This radio set
33957         * @param {Roo.bootstrap.Radio} item The checked item
33958         * @param {Roo.EventObject} e The event object
33959         */
33960        click : true
33961     });
33962     
33963 };
33964
33965 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33966
33967     radioes : false,
33968     
33969     inline : true,
33970     
33971     weight : '',
33972     
33973     indicatorpos : 'left',
33974     
33975     getAutoCreate : function()
33976     {
33977         var label = {
33978             tag : 'label',
33979             cls : 'roo-radio-set-label',
33980             cn : [
33981                 {
33982                     tag : 'span',
33983                     html : this.fieldLabel
33984                 }
33985             ]
33986         };
33987         
33988         if(this.indicatorpos == 'left'){
33989             label.cn.unshift({
33990                 tag : 'i',
33991                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33992                 tooltip : 'This field is required'
33993             });
33994         } else {
33995             label.cn.push({
33996                 tag : 'i',
33997                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33998                 tooltip : 'This field is required'
33999             });
34000         }
34001         
34002         var items = {
34003             tag : 'div',
34004             cls : 'roo-radio-set-items'
34005         };
34006         
34007         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34008         
34009         if (align === 'left' && this.fieldLabel.length) {
34010             
34011             items = {
34012                 cls : "roo-radio-set-right", 
34013                 cn: [
34014                     items
34015                 ]
34016             };
34017             
34018             if(this.labelWidth > 12){
34019                 label.style = "width: " + this.labelWidth + 'px';
34020             }
34021             
34022             if(this.labelWidth < 13 && this.labelmd == 0){
34023                 this.labelmd = this.labelWidth;
34024             }
34025             
34026             if(this.labellg > 0){
34027                 label.cls += ' col-lg-' + this.labellg;
34028                 items.cls += ' col-lg-' + (12 - this.labellg);
34029             }
34030             
34031             if(this.labelmd > 0){
34032                 label.cls += ' col-md-' + this.labelmd;
34033                 items.cls += ' col-md-' + (12 - this.labelmd);
34034             }
34035             
34036             if(this.labelsm > 0){
34037                 label.cls += ' col-sm-' + this.labelsm;
34038                 items.cls += ' col-sm-' + (12 - this.labelsm);
34039             }
34040             
34041             if(this.labelxs > 0){
34042                 label.cls += ' col-xs-' + this.labelxs;
34043                 items.cls += ' col-xs-' + (12 - this.labelxs);
34044             }
34045         }
34046         
34047         var cfg = {
34048             tag : 'div',
34049             cls : 'roo-radio-set',
34050             cn : [
34051                 {
34052                     tag : 'input',
34053                     cls : 'roo-radio-set-input',
34054                     type : 'hidden',
34055                     name : this.name,
34056                     value : this.value ? this.value :  ''
34057                 },
34058                 label,
34059                 items
34060             ]
34061         };
34062         
34063         if(this.weight.length){
34064             cfg.cls += ' roo-radio-' + this.weight;
34065         }
34066         
34067         if(this.inline) {
34068             cfg.cls += ' roo-radio-set-inline';
34069         }
34070         
34071         var settings=this;
34072         ['xs','sm','md','lg'].map(function(size){
34073             if (settings[size]) {
34074                 cfg.cls += ' col-' + size + '-' + settings[size];
34075             }
34076         });
34077         
34078         return cfg;
34079         
34080     },
34081
34082     initEvents : function()
34083     {
34084         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34085         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34086         
34087         if(!this.fieldLabel.length){
34088             this.labelEl.hide();
34089         }
34090         
34091         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34092         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34093         
34094         this.indicator = this.indicatorEl();
34095         
34096         if(this.indicator){
34097             this.indicator.addClass('invisible');
34098         }
34099         
34100         this.originalValue = this.getValue();
34101         
34102     },
34103     
34104     inputEl: function ()
34105     {
34106         return this.el.select('.roo-radio-set-input', true).first();
34107     },
34108     
34109     getChildContainer : function()
34110     {
34111         return this.itemsEl;
34112     },
34113     
34114     register : function(item)
34115     {
34116         this.radioes.push(item);
34117         
34118     },
34119     
34120     validate : function()
34121     {   
34122         if(this.getVisibilityEl().hasClass('hidden')){
34123             return true;
34124         }
34125         
34126         var valid = false;
34127         
34128         Roo.each(this.radioes, function(i){
34129             if(!i.checked){
34130                 return;
34131             }
34132             
34133             valid = true;
34134             return false;
34135         });
34136         
34137         if(this.allowBlank) {
34138             return true;
34139         }
34140         
34141         if(this.disabled || valid){
34142             this.markValid();
34143             return true;
34144         }
34145         
34146         this.markInvalid();
34147         return false;
34148         
34149     },
34150     
34151     markValid : function()
34152     {
34153         if(this.labelEl.isVisible(true)){
34154             this.indicatorEl().removeClass('visible');
34155             this.indicatorEl().addClass('invisible');
34156         }
34157         
34158         this.el.removeClass([this.invalidClass, this.validClass]);
34159         this.el.addClass(this.validClass);
34160         
34161         this.fireEvent('valid', this);
34162     },
34163     
34164     markInvalid : function(msg)
34165     {
34166         if(this.allowBlank || this.disabled){
34167             return;
34168         }
34169         
34170         if(this.labelEl.isVisible(true)){
34171             this.indicatorEl().removeClass('invisible');
34172             this.indicatorEl().addClass('visible');
34173         }
34174         
34175         this.el.removeClass([this.invalidClass, this.validClass]);
34176         this.el.addClass(this.invalidClass);
34177         
34178         this.fireEvent('invalid', this, msg);
34179         
34180     },
34181     
34182     setValue : function(v, suppressEvent)
34183     {   
34184         if(this.value === v){
34185             return;
34186         }
34187         
34188         this.value = v;
34189         
34190         if(this.rendered){
34191             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34192         }
34193         
34194         Roo.each(this.radioes, function(i){
34195             i.checked = false;
34196             i.el.removeClass('checked');
34197         });
34198         
34199         Roo.each(this.radioes, function(i){
34200             
34201             if(i.value === v || i.value.toString() === v.toString()){
34202                 i.checked = true;
34203                 i.el.addClass('checked');
34204                 
34205                 if(suppressEvent !== true){
34206                     this.fireEvent('check', this, i);
34207                 }
34208                 
34209                 return false;
34210             }
34211             
34212         }, this);
34213         
34214         this.validate();
34215     },
34216     
34217     clearInvalid : function(){
34218         
34219         if(!this.el || this.preventMark){
34220             return;
34221         }
34222         
34223         this.el.removeClass([this.invalidClass]);
34224         
34225         this.fireEvent('valid', this);
34226     }
34227     
34228 });
34229
34230 Roo.apply(Roo.bootstrap.RadioSet, {
34231     
34232     groups: {},
34233     
34234     register : function(set)
34235     {
34236         this.groups[set.name] = set;
34237     },
34238     
34239     get: function(name) 
34240     {
34241         if (typeof(this.groups[name]) == 'undefined') {
34242             return false;
34243         }
34244         
34245         return this.groups[name] ;
34246     }
34247     
34248 });
34249 /*
34250  * Based on:
34251  * Ext JS Library 1.1.1
34252  * Copyright(c) 2006-2007, Ext JS, LLC.
34253  *
34254  * Originally Released Under LGPL - original licence link has changed is not relivant.
34255  *
34256  * Fork - LGPL
34257  * <script type="text/javascript">
34258  */
34259
34260
34261 /**
34262  * @class Roo.bootstrap.SplitBar
34263  * @extends Roo.util.Observable
34264  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34265  * <br><br>
34266  * Usage:
34267  * <pre><code>
34268 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34269                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34270 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34271 split.minSize = 100;
34272 split.maxSize = 600;
34273 split.animate = true;
34274 split.on('moved', splitterMoved);
34275 </code></pre>
34276  * @constructor
34277  * Create a new SplitBar
34278  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34279  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34280  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34281  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34282                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34283                         position of the SplitBar).
34284  */
34285 Roo.bootstrap.SplitBar = function(cfg){
34286     
34287     /** @private */
34288     
34289     //{
34290     //  dragElement : elm
34291     //  resizingElement: el,
34292         // optional..
34293     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34294     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34295         // existingProxy ???
34296     //}
34297     
34298     this.el = Roo.get(cfg.dragElement, true);
34299     this.el.dom.unselectable = "on";
34300     /** @private */
34301     this.resizingEl = Roo.get(cfg.resizingElement, true);
34302
34303     /**
34304      * @private
34305      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34306      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34307      * @type Number
34308      */
34309     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34310     
34311     /**
34312      * The minimum size of the resizing element. (Defaults to 0)
34313      * @type Number
34314      */
34315     this.minSize = 0;
34316     
34317     /**
34318      * The maximum size of the resizing element. (Defaults to 2000)
34319      * @type Number
34320      */
34321     this.maxSize = 2000;
34322     
34323     /**
34324      * Whether to animate the transition to the new size
34325      * @type Boolean
34326      */
34327     this.animate = false;
34328     
34329     /**
34330      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34331      * @type Boolean
34332      */
34333     this.useShim = false;
34334     
34335     /** @private */
34336     this.shim = null;
34337     
34338     if(!cfg.existingProxy){
34339         /** @private */
34340         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34341     }else{
34342         this.proxy = Roo.get(cfg.existingProxy).dom;
34343     }
34344     /** @private */
34345     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34346     
34347     /** @private */
34348     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34349     
34350     /** @private */
34351     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34352     
34353     /** @private */
34354     this.dragSpecs = {};
34355     
34356     /**
34357      * @private The adapter to use to positon and resize elements
34358      */
34359     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34360     this.adapter.init(this);
34361     
34362     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34363         /** @private */
34364         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34365         this.el.addClass("roo-splitbar-h");
34366     }else{
34367         /** @private */
34368         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34369         this.el.addClass("roo-splitbar-v");
34370     }
34371     
34372     this.addEvents({
34373         /**
34374          * @event resize
34375          * Fires when the splitter is moved (alias for {@link #event-moved})
34376          * @param {Roo.bootstrap.SplitBar} this
34377          * @param {Number} newSize the new width or height
34378          */
34379         "resize" : true,
34380         /**
34381          * @event moved
34382          * Fires when the splitter is moved
34383          * @param {Roo.bootstrap.SplitBar} this
34384          * @param {Number} newSize the new width or height
34385          */
34386         "moved" : true,
34387         /**
34388          * @event beforeresize
34389          * Fires before the splitter is dragged
34390          * @param {Roo.bootstrap.SplitBar} this
34391          */
34392         "beforeresize" : true,
34393
34394         "beforeapply" : true
34395     });
34396
34397     Roo.util.Observable.call(this);
34398 };
34399
34400 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34401     onStartProxyDrag : function(x, y){
34402         this.fireEvent("beforeresize", this);
34403         if(!this.overlay){
34404             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34405             o.unselectable();
34406             o.enableDisplayMode("block");
34407             // all splitbars share the same overlay
34408             Roo.bootstrap.SplitBar.prototype.overlay = o;
34409         }
34410         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34411         this.overlay.show();
34412         Roo.get(this.proxy).setDisplayed("block");
34413         var size = this.adapter.getElementSize(this);
34414         this.activeMinSize = this.getMinimumSize();;
34415         this.activeMaxSize = this.getMaximumSize();;
34416         var c1 = size - this.activeMinSize;
34417         var c2 = Math.max(this.activeMaxSize - size, 0);
34418         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34419             this.dd.resetConstraints();
34420             this.dd.setXConstraint(
34421                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34422                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34423             );
34424             this.dd.setYConstraint(0, 0);
34425         }else{
34426             this.dd.resetConstraints();
34427             this.dd.setXConstraint(0, 0);
34428             this.dd.setYConstraint(
34429                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34430                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34431             );
34432          }
34433         this.dragSpecs.startSize = size;
34434         this.dragSpecs.startPoint = [x, y];
34435         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34436     },
34437     
34438     /** 
34439      * @private Called after the drag operation by the DDProxy
34440      */
34441     onEndProxyDrag : function(e){
34442         Roo.get(this.proxy).setDisplayed(false);
34443         var endPoint = Roo.lib.Event.getXY(e);
34444         if(this.overlay){
34445             this.overlay.hide();
34446         }
34447         var newSize;
34448         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34449             newSize = this.dragSpecs.startSize + 
34450                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34451                     endPoint[0] - this.dragSpecs.startPoint[0] :
34452                     this.dragSpecs.startPoint[0] - endPoint[0]
34453                 );
34454         }else{
34455             newSize = this.dragSpecs.startSize + 
34456                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34457                     endPoint[1] - this.dragSpecs.startPoint[1] :
34458                     this.dragSpecs.startPoint[1] - endPoint[1]
34459                 );
34460         }
34461         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34462         if(newSize != this.dragSpecs.startSize){
34463             if(this.fireEvent('beforeapply', this, newSize) !== false){
34464                 this.adapter.setElementSize(this, newSize);
34465                 this.fireEvent("moved", this, newSize);
34466                 this.fireEvent("resize", this, newSize);
34467             }
34468         }
34469     },
34470     
34471     /**
34472      * Get the adapter this SplitBar uses
34473      * @return The adapter object
34474      */
34475     getAdapter : function(){
34476         return this.adapter;
34477     },
34478     
34479     /**
34480      * Set the adapter this SplitBar uses
34481      * @param {Object} adapter A SplitBar adapter object
34482      */
34483     setAdapter : function(adapter){
34484         this.adapter = adapter;
34485         this.adapter.init(this);
34486     },
34487     
34488     /**
34489      * Gets the minimum size for the resizing element
34490      * @return {Number} The minimum size
34491      */
34492     getMinimumSize : function(){
34493         return this.minSize;
34494     },
34495     
34496     /**
34497      * Sets the minimum size for the resizing element
34498      * @param {Number} minSize The minimum size
34499      */
34500     setMinimumSize : function(minSize){
34501         this.minSize = minSize;
34502     },
34503     
34504     /**
34505      * Gets the maximum size for the resizing element
34506      * @return {Number} The maximum size
34507      */
34508     getMaximumSize : function(){
34509         return this.maxSize;
34510     },
34511     
34512     /**
34513      * Sets the maximum size for the resizing element
34514      * @param {Number} maxSize The maximum size
34515      */
34516     setMaximumSize : function(maxSize){
34517         this.maxSize = maxSize;
34518     },
34519     
34520     /**
34521      * Sets the initialize size for the resizing element
34522      * @param {Number} size The initial size
34523      */
34524     setCurrentSize : function(size){
34525         var oldAnimate = this.animate;
34526         this.animate = false;
34527         this.adapter.setElementSize(this, size);
34528         this.animate = oldAnimate;
34529     },
34530     
34531     /**
34532      * Destroy this splitbar. 
34533      * @param {Boolean} removeEl True to remove the element
34534      */
34535     destroy : function(removeEl){
34536         if(this.shim){
34537             this.shim.remove();
34538         }
34539         this.dd.unreg();
34540         this.proxy.parentNode.removeChild(this.proxy);
34541         if(removeEl){
34542             this.el.remove();
34543         }
34544     }
34545 });
34546
34547 /**
34548  * @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.
34549  */
34550 Roo.bootstrap.SplitBar.createProxy = function(dir){
34551     var proxy = new Roo.Element(document.createElement("div"));
34552     proxy.unselectable();
34553     var cls = 'roo-splitbar-proxy';
34554     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34555     document.body.appendChild(proxy.dom);
34556     return proxy.dom;
34557 };
34558
34559 /** 
34560  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34561  * Default Adapter. It assumes the splitter and resizing element are not positioned
34562  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34563  */
34564 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34565 };
34566
34567 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34568     // do nothing for now
34569     init : function(s){
34570     
34571     },
34572     /**
34573      * Called before drag operations to get the current size of the resizing element. 
34574      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34575      */
34576      getElementSize : function(s){
34577         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34578             return s.resizingEl.getWidth();
34579         }else{
34580             return s.resizingEl.getHeight();
34581         }
34582     },
34583     
34584     /**
34585      * Called after drag operations to set the size of the resizing element.
34586      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34587      * @param {Number} newSize The new size to set
34588      * @param {Function} onComplete A function to be invoked when resizing is complete
34589      */
34590     setElementSize : function(s, newSize, onComplete){
34591         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34592             if(!s.animate){
34593                 s.resizingEl.setWidth(newSize);
34594                 if(onComplete){
34595                     onComplete(s, newSize);
34596                 }
34597             }else{
34598                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34599             }
34600         }else{
34601             
34602             if(!s.animate){
34603                 s.resizingEl.setHeight(newSize);
34604                 if(onComplete){
34605                     onComplete(s, newSize);
34606                 }
34607             }else{
34608                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34609             }
34610         }
34611     }
34612 };
34613
34614 /** 
34615  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34616  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34617  * Adapter that  moves the splitter element to align with the resized sizing element. 
34618  * Used with an absolute positioned SplitBar.
34619  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34620  * document.body, make sure you assign an id to the body element.
34621  */
34622 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34623     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34624     this.container = Roo.get(container);
34625 };
34626
34627 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34628     init : function(s){
34629         this.basic.init(s);
34630     },
34631     
34632     getElementSize : function(s){
34633         return this.basic.getElementSize(s);
34634     },
34635     
34636     setElementSize : function(s, newSize, onComplete){
34637         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34638     },
34639     
34640     moveSplitter : function(s){
34641         var yes = Roo.bootstrap.SplitBar;
34642         switch(s.placement){
34643             case yes.LEFT:
34644                 s.el.setX(s.resizingEl.getRight());
34645                 break;
34646             case yes.RIGHT:
34647                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34648                 break;
34649             case yes.TOP:
34650                 s.el.setY(s.resizingEl.getBottom());
34651                 break;
34652             case yes.BOTTOM:
34653                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34654                 break;
34655         }
34656     }
34657 };
34658
34659 /**
34660  * Orientation constant - Create a vertical SplitBar
34661  * @static
34662  * @type Number
34663  */
34664 Roo.bootstrap.SplitBar.VERTICAL = 1;
34665
34666 /**
34667  * Orientation constant - Create a horizontal SplitBar
34668  * @static
34669  * @type Number
34670  */
34671 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34672
34673 /**
34674  * Placement constant - The resizing element is to the left of the splitter element
34675  * @static
34676  * @type Number
34677  */
34678 Roo.bootstrap.SplitBar.LEFT = 1;
34679
34680 /**
34681  * Placement constant - The resizing element is to the right of the splitter element
34682  * @static
34683  * @type Number
34684  */
34685 Roo.bootstrap.SplitBar.RIGHT = 2;
34686
34687 /**
34688  * Placement constant - The resizing element is positioned above the splitter element
34689  * @static
34690  * @type Number
34691  */
34692 Roo.bootstrap.SplitBar.TOP = 3;
34693
34694 /**
34695  * Placement constant - The resizing element is positioned under splitter element
34696  * @static
34697  * @type Number
34698  */
34699 Roo.bootstrap.SplitBar.BOTTOM = 4;
34700 Roo.namespace("Roo.bootstrap.layout");/*
34701  * Based on:
34702  * Ext JS Library 1.1.1
34703  * Copyright(c) 2006-2007, Ext JS, LLC.
34704  *
34705  * Originally Released Under LGPL - original licence link has changed is not relivant.
34706  *
34707  * Fork - LGPL
34708  * <script type="text/javascript">
34709  */
34710
34711 /**
34712  * @class Roo.bootstrap.layout.Manager
34713  * @extends Roo.bootstrap.Component
34714  * Base class for layout managers.
34715  */
34716 Roo.bootstrap.layout.Manager = function(config)
34717 {
34718     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34719
34720
34721
34722
34723
34724     /** false to disable window resize monitoring @type Boolean */
34725     this.monitorWindowResize = true;
34726     this.regions = {};
34727     this.addEvents({
34728         /**
34729          * @event layout
34730          * Fires when a layout is performed.
34731          * @param {Roo.LayoutManager} this
34732          */
34733         "layout" : true,
34734         /**
34735          * @event regionresized
34736          * Fires when the user resizes a region.
34737          * @param {Roo.LayoutRegion} region The resized region
34738          * @param {Number} newSize The new size (width for east/west, height for north/south)
34739          */
34740         "regionresized" : true,
34741         /**
34742          * @event regioncollapsed
34743          * Fires when a region is collapsed.
34744          * @param {Roo.LayoutRegion} region The collapsed region
34745          */
34746         "regioncollapsed" : true,
34747         /**
34748          * @event regionexpanded
34749          * Fires when a region is expanded.
34750          * @param {Roo.LayoutRegion} region The expanded region
34751          */
34752         "regionexpanded" : true
34753     });
34754     this.updating = false;
34755
34756     if (config.el) {
34757         this.el = Roo.get(config.el);
34758         this.initEvents();
34759     }
34760
34761 };
34762
34763 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34764
34765
34766     regions : null,
34767
34768     monitorWindowResize : true,
34769
34770
34771     updating : false,
34772
34773
34774     onRender : function(ct, position)
34775     {
34776         if(!this.el){
34777             this.el = Roo.get(ct);
34778             this.initEvents();
34779         }
34780         //this.fireEvent('render',this);
34781     },
34782
34783
34784     initEvents: function()
34785     {
34786
34787
34788         // ie scrollbar fix
34789         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34790             document.body.scroll = "no";
34791         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34792             this.el.position('relative');
34793         }
34794         this.id = this.el.id;
34795         this.el.addClass("roo-layout-container");
34796         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34797         if(this.el.dom != document.body ) {
34798             this.el.on('resize', this.layout,this);
34799             this.el.on('show', this.layout,this);
34800         }
34801
34802     },
34803
34804     /**
34805      * Returns true if this layout is currently being updated
34806      * @return {Boolean}
34807      */
34808     isUpdating : function(){
34809         return this.updating;
34810     },
34811
34812     /**
34813      * Suspend the LayoutManager from doing auto-layouts while
34814      * making multiple add or remove calls
34815      */
34816     beginUpdate : function(){
34817         this.updating = true;
34818     },
34819
34820     /**
34821      * Restore auto-layouts and optionally disable the manager from performing a layout
34822      * @param {Boolean} noLayout true to disable a layout update
34823      */
34824     endUpdate : function(noLayout){
34825         this.updating = false;
34826         if(!noLayout){
34827             this.layout();
34828         }
34829     },
34830
34831     layout: function(){
34832         // abstract...
34833     },
34834
34835     onRegionResized : function(region, newSize){
34836         this.fireEvent("regionresized", region, newSize);
34837         this.layout();
34838     },
34839
34840     onRegionCollapsed : function(region){
34841         this.fireEvent("regioncollapsed", region);
34842     },
34843
34844     onRegionExpanded : function(region){
34845         this.fireEvent("regionexpanded", region);
34846     },
34847
34848     /**
34849      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34850      * performs box-model adjustments.
34851      * @return {Object} The size as an object {width: (the width), height: (the height)}
34852      */
34853     getViewSize : function()
34854     {
34855         var size;
34856         if(this.el.dom != document.body){
34857             size = this.el.getSize();
34858         }else{
34859             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34860         }
34861         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34862         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34863         return size;
34864     },
34865
34866     /**
34867      * Returns the Element this layout is bound to.
34868      * @return {Roo.Element}
34869      */
34870     getEl : function(){
34871         return this.el;
34872     },
34873
34874     /**
34875      * Returns the specified region.
34876      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34877      * @return {Roo.LayoutRegion}
34878      */
34879     getRegion : function(target){
34880         return this.regions[target.toLowerCase()];
34881     },
34882
34883     onWindowResize : function(){
34884         if(this.monitorWindowResize){
34885             this.layout();
34886         }
34887     }
34888 });
34889 /*
34890  * Based on:
34891  * Ext JS Library 1.1.1
34892  * Copyright(c) 2006-2007, Ext JS, LLC.
34893  *
34894  * Originally Released Under LGPL - original licence link has changed is not relivant.
34895  *
34896  * Fork - LGPL
34897  * <script type="text/javascript">
34898  */
34899 /**
34900  * @class Roo.bootstrap.layout.Border
34901  * @extends Roo.bootstrap.layout.Manager
34902  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34903  * please see: examples/bootstrap/nested.html<br><br>
34904  
34905 <b>The container the layout is rendered into can be either the body element or any other element.
34906 If it is not the body element, the container needs to either be an absolute positioned element,
34907 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34908 the container size if it is not the body element.</b>
34909
34910 * @constructor
34911 * Create a new Border
34912 * @param {Object} config Configuration options
34913  */
34914 Roo.bootstrap.layout.Border = function(config){
34915     config = config || {};
34916     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34917     
34918     
34919     
34920     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34921         if(config[region]){
34922             config[region].region = region;
34923             this.addRegion(config[region]);
34924         }
34925     },this);
34926     
34927 };
34928
34929 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34930
34931 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34932     /**
34933      * Creates and adds a new region if it doesn't already exist.
34934      * @param {String} target The target region key (north, south, east, west or center).
34935      * @param {Object} config The regions config object
34936      * @return {BorderLayoutRegion} The new region
34937      */
34938     addRegion : function(config)
34939     {
34940         if(!this.regions[config.region]){
34941             var r = this.factory(config);
34942             this.bindRegion(r);
34943         }
34944         return this.regions[config.region];
34945     },
34946
34947     // private (kinda)
34948     bindRegion : function(r){
34949         this.regions[r.config.region] = r;
34950         
34951         r.on("visibilitychange",    this.layout, this);
34952         r.on("paneladded",          this.layout, this);
34953         r.on("panelremoved",        this.layout, this);
34954         r.on("invalidated",         this.layout, this);
34955         r.on("resized",             this.onRegionResized, this);
34956         r.on("collapsed",           this.onRegionCollapsed, this);
34957         r.on("expanded",            this.onRegionExpanded, this);
34958     },
34959
34960     /**
34961      * Performs a layout update.
34962      */
34963     layout : function()
34964     {
34965         if(this.updating) {
34966             return;
34967         }
34968         
34969         // render all the rebions if they have not been done alreayd?
34970         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34971             if(this.regions[region] && !this.regions[region].bodyEl){
34972                 this.regions[region].onRender(this.el)
34973             }
34974         },this);
34975         
34976         var size = this.getViewSize();
34977         var w = size.width;
34978         var h = size.height;
34979         var centerW = w;
34980         var centerH = h;
34981         var centerY = 0;
34982         var centerX = 0;
34983         //var x = 0, y = 0;
34984
34985         var rs = this.regions;
34986         var north = rs["north"];
34987         var south = rs["south"]; 
34988         var west = rs["west"];
34989         var east = rs["east"];
34990         var center = rs["center"];
34991         //if(this.hideOnLayout){ // not supported anymore
34992             //c.el.setStyle("display", "none");
34993         //}
34994         if(north && north.isVisible()){
34995             var b = north.getBox();
34996             var m = north.getMargins();
34997             b.width = w - (m.left+m.right);
34998             b.x = m.left;
34999             b.y = m.top;
35000             centerY = b.height + b.y + m.bottom;
35001             centerH -= centerY;
35002             north.updateBox(this.safeBox(b));
35003         }
35004         if(south && south.isVisible()){
35005             var b = south.getBox();
35006             var m = south.getMargins();
35007             b.width = w - (m.left+m.right);
35008             b.x = m.left;
35009             var totalHeight = (b.height + m.top + m.bottom);
35010             b.y = h - totalHeight + m.top;
35011             centerH -= totalHeight;
35012             south.updateBox(this.safeBox(b));
35013         }
35014         if(west && west.isVisible()){
35015             var b = west.getBox();
35016             var m = west.getMargins();
35017             b.height = centerH - (m.top+m.bottom);
35018             b.x = m.left;
35019             b.y = centerY + m.top;
35020             var totalWidth = (b.width + m.left + m.right);
35021             centerX += totalWidth;
35022             centerW -= totalWidth;
35023             west.updateBox(this.safeBox(b));
35024         }
35025         if(east && east.isVisible()){
35026             var b = east.getBox();
35027             var m = east.getMargins();
35028             b.height = centerH - (m.top+m.bottom);
35029             var totalWidth = (b.width + m.left + m.right);
35030             b.x = w - totalWidth + m.left;
35031             b.y = centerY + m.top;
35032             centerW -= totalWidth;
35033             east.updateBox(this.safeBox(b));
35034         }
35035         if(center){
35036             var m = center.getMargins();
35037             var centerBox = {
35038                 x: centerX + m.left,
35039                 y: centerY + m.top,
35040                 width: centerW - (m.left+m.right),
35041                 height: centerH - (m.top+m.bottom)
35042             };
35043             //if(this.hideOnLayout){
35044                 //center.el.setStyle("display", "block");
35045             //}
35046             center.updateBox(this.safeBox(centerBox));
35047         }
35048         this.el.repaint();
35049         this.fireEvent("layout", this);
35050     },
35051
35052     // private
35053     safeBox : function(box){
35054         box.width = Math.max(0, box.width);
35055         box.height = Math.max(0, box.height);
35056         return box;
35057     },
35058
35059     /**
35060      * Adds a ContentPanel (or subclass) to this layout.
35061      * @param {String} target The target region key (north, south, east, west or center).
35062      * @param {Roo.ContentPanel} panel The panel to add
35063      * @return {Roo.ContentPanel} The added panel
35064      */
35065     add : function(target, panel){
35066          
35067         target = target.toLowerCase();
35068         return this.regions[target].add(panel);
35069     },
35070
35071     /**
35072      * Remove a ContentPanel (or subclass) to this layout.
35073      * @param {String} target The target region key (north, south, east, west or center).
35074      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35075      * @return {Roo.ContentPanel} The removed panel
35076      */
35077     remove : function(target, panel){
35078         target = target.toLowerCase();
35079         return this.regions[target].remove(panel);
35080     },
35081
35082     /**
35083      * Searches all regions for a panel with the specified id
35084      * @param {String} panelId
35085      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35086      */
35087     findPanel : function(panelId){
35088         var rs = this.regions;
35089         for(var target in rs){
35090             if(typeof rs[target] != "function"){
35091                 var p = rs[target].getPanel(panelId);
35092                 if(p){
35093                     return p;
35094                 }
35095             }
35096         }
35097         return null;
35098     },
35099
35100     /**
35101      * Searches all regions for a panel with the specified id and activates (shows) it.
35102      * @param {String/ContentPanel} panelId The panels id or the panel itself
35103      * @return {Roo.ContentPanel} The shown panel or null
35104      */
35105     showPanel : function(panelId) {
35106       var rs = this.regions;
35107       for(var target in rs){
35108          var r = rs[target];
35109          if(typeof r != "function"){
35110             if(r.hasPanel(panelId)){
35111                return r.showPanel(panelId);
35112             }
35113          }
35114       }
35115       return null;
35116    },
35117
35118    /**
35119      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35120      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35121      */
35122    /*
35123     restoreState : function(provider){
35124         if(!provider){
35125             provider = Roo.state.Manager;
35126         }
35127         var sm = new Roo.LayoutStateManager();
35128         sm.init(this, provider);
35129     },
35130 */
35131  
35132  
35133     /**
35134      * Adds a xtype elements to the layout.
35135      * <pre><code>
35136
35137 layout.addxtype({
35138        xtype : 'ContentPanel',
35139        region: 'west',
35140        items: [ .... ]
35141    }
35142 );
35143
35144 layout.addxtype({
35145         xtype : 'NestedLayoutPanel',
35146         region: 'west',
35147         layout: {
35148            center: { },
35149            west: { }   
35150         },
35151         items : [ ... list of content panels or nested layout panels.. ]
35152    }
35153 );
35154 </code></pre>
35155      * @param {Object} cfg Xtype definition of item to add.
35156      */
35157     addxtype : function(cfg)
35158     {
35159         // basically accepts a pannel...
35160         // can accept a layout region..!?!?
35161         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35162         
35163         
35164         // theory?  children can only be panels??
35165         
35166         //if (!cfg.xtype.match(/Panel$/)) {
35167         //    return false;
35168         //}
35169         var ret = false;
35170         
35171         if (typeof(cfg.region) == 'undefined') {
35172             Roo.log("Failed to add Panel, region was not set");
35173             Roo.log(cfg);
35174             return false;
35175         }
35176         var region = cfg.region;
35177         delete cfg.region;
35178         
35179           
35180         var xitems = [];
35181         if (cfg.items) {
35182             xitems = cfg.items;
35183             delete cfg.items;
35184         }
35185         var nb = false;
35186         
35187         switch(cfg.xtype) 
35188         {
35189             case 'Content':  // ContentPanel (el, cfg)
35190             case 'Scroll':  // ContentPanel (el, cfg)
35191             case 'View': 
35192                 cfg.autoCreate = true;
35193                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35194                 //} else {
35195                 //    var el = this.el.createChild();
35196                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35197                 //}
35198                 
35199                 this.add(region, ret);
35200                 break;
35201             
35202             /*
35203             case 'TreePanel': // our new panel!
35204                 cfg.el = this.el.createChild();
35205                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35206                 this.add(region, ret);
35207                 break;
35208             */
35209             
35210             case 'Nest': 
35211                 // create a new Layout (which is  a Border Layout...
35212                 
35213                 var clayout = cfg.layout;
35214                 clayout.el  = this.el.createChild();
35215                 clayout.items   = clayout.items  || [];
35216                 
35217                 delete cfg.layout;
35218                 
35219                 // replace this exitems with the clayout ones..
35220                 xitems = clayout.items;
35221                  
35222                 // force background off if it's in center...
35223                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35224                     cfg.background = false;
35225                 }
35226                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35227                 
35228                 
35229                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35230                 //console.log('adding nested layout panel '  + cfg.toSource());
35231                 this.add(region, ret);
35232                 nb = {}; /// find first...
35233                 break;
35234             
35235             case 'Grid':
35236                 
35237                 // needs grid and region
35238                 
35239                 //var el = this.getRegion(region).el.createChild();
35240                 /*
35241                  *var el = this.el.createChild();
35242                 // create the grid first...
35243                 cfg.grid.container = el;
35244                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35245                 */
35246                 
35247                 if (region == 'center' && this.active ) {
35248                     cfg.background = false;
35249                 }
35250                 
35251                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35252                 
35253                 this.add(region, ret);
35254                 /*
35255                 if (cfg.background) {
35256                     // render grid on panel activation (if panel background)
35257                     ret.on('activate', function(gp) {
35258                         if (!gp.grid.rendered) {
35259                     //        gp.grid.render(el);
35260                         }
35261                     });
35262                 } else {
35263                   //  cfg.grid.render(el);
35264                 }
35265                 */
35266                 break;
35267            
35268            
35269             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35270                 // it was the old xcomponent building that caused this before.
35271                 // espeically if border is the top element in the tree.
35272                 ret = this;
35273                 break; 
35274                 
35275                     
35276                 
35277                 
35278                 
35279             default:
35280                 /*
35281                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35282                     
35283                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35284                     this.add(region, ret);
35285                 } else {
35286                 */
35287                     Roo.log(cfg);
35288                     throw "Can not add '" + cfg.xtype + "' to Border";
35289                     return null;
35290              
35291                                 
35292              
35293         }
35294         this.beginUpdate();
35295         // add children..
35296         var region = '';
35297         var abn = {};
35298         Roo.each(xitems, function(i)  {
35299             region = nb && i.region ? i.region : false;
35300             
35301             var add = ret.addxtype(i);
35302            
35303             if (region) {
35304                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35305                 if (!i.background) {
35306                     abn[region] = nb[region] ;
35307                 }
35308             }
35309             
35310         });
35311         this.endUpdate();
35312
35313         // make the last non-background panel active..
35314         //if (nb) { Roo.log(abn); }
35315         if (nb) {
35316             
35317             for(var r in abn) {
35318                 region = this.getRegion(r);
35319                 if (region) {
35320                     // tried using nb[r], but it does not work..
35321                      
35322                     region.showPanel(abn[r]);
35323                    
35324                 }
35325             }
35326         }
35327         return ret;
35328         
35329     },
35330     
35331     
35332 // private
35333     factory : function(cfg)
35334     {
35335         
35336         var validRegions = Roo.bootstrap.layout.Border.regions;
35337
35338         var target = cfg.region;
35339         cfg.mgr = this;
35340         
35341         var r = Roo.bootstrap.layout;
35342         Roo.log(target);
35343         switch(target){
35344             case "north":
35345                 return new r.North(cfg);
35346             case "south":
35347                 return new r.South(cfg);
35348             case "east":
35349                 return new r.East(cfg);
35350             case "west":
35351                 return new r.West(cfg);
35352             case "center":
35353                 return new r.Center(cfg);
35354         }
35355         throw 'Layout region "'+target+'" not supported.';
35356     }
35357     
35358     
35359 });
35360  /*
35361  * Based on:
35362  * Ext JS Library 1.1.1
35363  * Copyright(c) 2006-2007, Ext JS, LLC.
35364  *
35365  * Originally Released Under LGPL - original licence link has changed is not relivant.
35366  *
35367  * Fork - LGPL
35368  * <script type="text/javascript">
35369  */
35370  
35371 /**
35372  * @class Roo.bootstrap.layout.Basic
35373  * @extends Roo.util.Observable
35374  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35375  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35376  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35377  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35378  * @cfg {string}   region  the region that it inhabits..
35379  * @cfg {bool}   skipConfig skip config?
35380  * 
35381
35382  */
35383 Roo.bootstrap.layout.Basic = function(config){
35384     
35385     this.mgr = config.mgr;
35386     
35387     this.position = config.region;
35388     
35389     var skipConfig = config.skipConfig;
35390     
35391     this.events = {
35392         /**
35393          * @scope Roo.BasicLayoutRegion
35394          */
35395         
35396         /**
35397          * @event beforeremove
35398          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35399          * @param {Roo.LayoutRegion} this
35400          * @param {Roo.ContentPanel} panel The panel
35401          * @param {Object} e The cancel event object
35402          */
35403         "beforeremove" : true,
35404         /**
35405          * @event invalidated
35406          * Fires when the layout for this region is changed.
35407          * @param {Roo.LayoutRegion} this
35408          */
35409         "invalidated" : true,
35410         /**
35411          * @event visibilitychange
35412          * Fires when this region is shown or hidden 
35413          * @param {Roo.LayoutRegion} this
35414          * @param {Boolean} visibility true or false
35415          */
35416         "visibilitychange" : true,
35417         /**
35418          * @event paneladded
35419          * Fires when a panel is added. 
35420          * @param {Roo.LayoutRegion} this
35421          * @param {Roo.ContentPanel} panel The panel
35422          */
35423         "paneladded" : true,
35424         /**
35425          * @event panelremoved
35426          * Fires when a panel is removed. 
35427          * @param {Roo.LayoutRegion} this
35428          * @param {Roo.ContentPanel} panel The panel
35429          */
35430         "panelremoved" : true,
35431         /**
35432          * @event beforecollapse
35433          * Fires when this region before collapse.
35434          * @param {Roo.LayoutRegion} this
35435          */
35436         "beforecollapse" : true,
35437         /**
35438          * @event collapsed
35439          * Fires when this region is collapsed.
35440          * @param {Roo.LayoutRegion} this
35441          */
35442         "collapsed" : true,
35443         /**
35444          * @event expanded
35445          * Fires when this region is expanded.
35446          * @param {Roo.LayoutRegion} this
35447          */
35448         "expanded" : true,
35449         /**
35450          * @event slideshow
35451          * Fires when this region is slid into view.
35452          * @param {Roo.LayoutRegion} this
35453          */
35454         "slideshow" : true,
35455         /**
35456          * @event slidehide
35457          * Fires when this region slides out of view. 
35458          * @param {Roo.LayoutRegion} this
35459          */
35460         "slidehide" : true,
35461         /**
35462          * @event panelactivated
35463          * Fires when a panel is activated. 
35464          * @param {Roo.LayoutRegion} this
35465          * @param {Roo.ContentPanel} panel The activated panel
35466          */
35467         "panelactivated" : true,
35468         /**
35469          * @event resized
35470          * Fires when the user resizes this region. 
35471          * @param {Roo.LayoutRegion} this
35472          * @param {Number} newSize The new size (width for east/west, height for north/south)
35473          */
35474         "resized" : true
35475     };
35476     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35477     this.panels = new Roo.util.MixedCollection();
35478     this.panels.getKey = this.getPanelId.createDelegate(this);
35479     this.box = null;
35480     this.activePanel = null;
35481     // ensure listeners are added...
35482     
35483     if (config.listeners || config.events) {
35484         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35485             listeners : config.listeners || {},
35486             events : config.events || {}
35487         });
35488     }
35489     
35490     if(skipConfig !== true){
35491         this.applyConfig(config);
35492     }
35493 };
35494
35495 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35496 {
35497     getPanelId : function(p){
35498         return p.getId();
35499     },
35500     
35501     applyConfig : function(config){
35502         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35503         this.config = config;
35504         
35505     },
35506     
35507     /**
35508      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35509      * the width, for horizontal (north, south) the height.
35510      * @param {Number} newSize The new width or height
35511      */
35512     resizeTo : function(newSize){
35513         var el = this.el ? this.el :
35514                  (this.activePanel ? this.activePanel.getEl() : null);
35515         if(el){
35516             switch(this.position){
35517                 case "east":
35518                 case "west":
35519                     el.setWidth(newSize);
35520                     this.fireEvent("resized", this, newSize);
35521                 break;
35522                 case "north":
35523                 case "south":
35524                     el.setHeight(newSize);
35525                     this.fireEvent("resized", this, newSize);
35526                 break;                
35527             }
35528         }
35529     },
35530     
35531     getBox : function(){
35532         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35533     },
35534     
35535     getMargins : function(){
35536         return this.margins;
35537     },
35538     
35539     updateBox : function(box){
35540         this.box = box;
35541         var el = this.activePanel.getEl();
35542         el.dom.style.left = box.x + "px";
35543         el.dom.style.top = box.y + "px";
35544         this.activePanel.setSize(box.width, box.height);
35545     },
35546     
35547     /**
35548      * Returns the container element for this region.
35549      * @return {Roo.Element}
35550      */
35551     getEl : function(){
35552         return this.activePanel;
35553     },
35554     
35555     /**
35556      * Returns true if this region is currently visible.
35557      * @return {Boolean}
35558      */
35559     isVisible : function(){
35560         return this.activePanel ? true : false;
35561     },
35562     
35563     setActivePanel : function(panel){
35564         panel = this.getPanel(panel);
35565         if(this.activePanel && this.activePanel != panel){
35566             this.activePanel.setActiveState(false);
35567             this.activePanel.getEl().setLeftTop(-10000,-10000);
35568         }
35569         this.activePanel = panel;
35570         panel.setActiveState(true);
35571         if(this.box){
35572             panel.setSize(this.box.width, this.box.height);
35573         }
35574         this.fireEvent("panelactivated", this, panel);
35575         this.fireEvent("invalidated");
35576     },
35577     
35578     /**
35579      * Show the specified panel.
35580      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35581      * @return {Roo.ContentPanel} The shown panel or null
35582      */
35583     showPanel : function(panel){
35584         panel = this.getPanel(panel);
35585         if(panel){
35586             this.setActivePanel(panel);
35587         }
35588         return panel;
35589     },
35590     
35591     /**
35592      * Get the active panel for this region.
35593      * @return {Roo.ContentPanel} The active panel or null
35594      */
35595     getActivePanel : function(){
35596         return this.activePanel;
35597     },
35598     
35599     /**
35600      * Add the passed ContentPanel(s)
35601      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35602      * @return {Roo.ContentPanel} The panel added (if only one was added)
35603      */
35604     add : function(panel){
35605         if(arguments.length > 1){
35606             for(var i = 0, len = arguments.length; i < len; i++) {
35607                 this.add(arguments[i]);
35608             }
35609             return null;
35610         }
35611         if(this.hasPanel(panel)){
35612             this.showPanel(panel);
35613             return panel;
35614         }
35615         var el = panel.getEl();
35616         if(el.dom.parentNode != this.mgr.el.dom){
35617             this.mgr.el.dom.appendChild(el.dom);
35618         }
35619         if(panel.setRegion){
35620             panel.setRegion(this);
35621         }
35622         this.panels.add(panel);
35623         el.setStyle("position", "absolute");
35624         if(!panel.background){
35625             this.setActivePanel(panel);
35626             if(this.config.initialSize && this.panels.getCount()==1){
35627                 this.resizeTo(this.config.initialSize);
35628             }
35629         }
35630         this.fireEvent("paneladded", this, panel);
35631         return panel;
35632     },
35633     
35634     /**
35635      * Returns true if the panel is in this region.
35636      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35637      * @return {Boolean}
35638      */
35639     hasPanel : function(panel){
35640         if(typeof panel == "object"){ // must be panel obj
35641             panel = panel.getId();
35642         }
35643         return this.getPanel(panel) ? true : false;
35644     },
35645     
35646     /**
35647      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35648      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35649      * @param {Boolean} preservePanel Overrides the config preservePanel option
35650      * @return {Roo.ContentPanel} The panel that was removed
35651      */
35652     remove : function(panel, preservePanel){
35653         panel = this.getPanel(panel);
35654         if(!panel){
35655             return null;
35656         }
35657         var e = {};
35658         this.fireEvent("beforeremove", this, panel, e);
35659         if(e.cancel === true){
35660             return null;
35661         }
35662         var panelId = panel.getId();
35663         this.panels.removeKey(panelId);
35664         return panel;
35665     },
35666     
35667     /**
35668      * Returns the panel specified or null if it's not in this region.
35669      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35670      * @return {Roo.ContentPanel}
35671      */
35672     getPanel : function(id){
35673         if(typeof id == "object"){ // must be panel obj
35674             return id;
35675         }
35676         return this.panels.get(id);
35677     },
35678     
35679     /**
35680      * Returns this regions position (north/south/east/west/center).
35681      * @return {String} 
35682      */
35683     getPosition: function(){
35684         return this.position;    
35685     }
35686 });/*
35687  * Based on:
35688  * Ext JS Library 1.1.1
35689  * Copyright(c) 2006-2007, Ext JS, LLC.
35690  *
35691  * Originally Released Under LGPL - original licence link has changed is not relivant.
35692  *
35693  * Fork - LGPL
35694  * <script type="text/javascript">
35695  */
35696  
35697 /**
35698  * @class Roo.bootstrap.layout.Region
35699  * @extends Roo.bootstrap.layout.Basic
35700  * This class represents a region in a layout manager.
35701  
35702  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35703  * @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})
35704  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35705  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35706  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35707  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35708  * @cfg {String}    title           The title for the region (overrides panel titles)
35709  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35710  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35711  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35712  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35713  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35714  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35715  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35716  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35717  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35718  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35719
35720  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35721  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35722  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35723  * @cfg {Number}    width           For East/West panels
35724  * @cfg {Number}    height          For North/South panels
35725  * @cfg {Boolean}   split           To show the splitter
35726  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35727  * 
35728  * @cfg {string}   cls             Extra CSS classes to add to region
35729  * 
35730  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35731  * @cfg {string}   region  the region that it inhabits..
35732  *
35733
35734  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35735  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35736
35737  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35738  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35739  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35740  */
35741 Roo.bootstrap.layout.Region = function(config)
35742 {
35743     this.applyConfig(config);
35744
35745     var mgr = config.mgr;
35746     var pos = config.region;
35747     config.skipConfig = true;
35748     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35749     
35750     if (mgr.el) {
35751         this.onRender(mgr.el);   
35752     }
35753      
35754     this.visible = true;
35755     this.collapsed = false;
35756     this.unrendered_panels = [];
35757 };
35758
35759 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35760
35761     position: '', // set by wrapper (eg. north/south etc..)
35762     unrendered_panels : null,  // unrendered panels.
35763     createBody : function(){
35764         /** This region's body element 
35765         * @type Roo.Element */
35766         this.bodyEl = this.el.createChild({
35767                 tag: "div",
35768                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35769         });
35770     },
35771
35772     onRender: function(ctr, pos)
35773     {
35774         var dh = Roo.DomHelper;
35775         /** This region's container element 
35776         * @type Roo.Element */
35777         this.el = dh.append(ctr.dom, {
35778                 tag: "div",
35779                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35780             }, true);
35781         /** This region's title element 
35782         * @type Roo.Element */
35783     
35784         this.titleEl = dh.append(this.el.dom,
35785             {
35786                     tag: "div",
35787                     unselectable: "on",
35788                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35789                     children:[
35790                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35791                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35792                     ]}, true);
35793         
35794         this.titleEl.enableDisplayMode();
35795         /** This region's title text element 
35796         * @type HTMLElement */
35797         this.titleTextEl = this.titleEl.dom.firstChild;
35798         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35799         /*
35800         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35801         this.closeBtn.enableDisplayMode();
35802         this.closeBtn.on("click", this.closeClicked, this);
35803         this.closeBtn.hide();
35804     */
35805         this.createBody(this.config);
35806         if(this.config.hideWhenEmpty){
35807             this.hide();
35808             this.on("paneladded", this.validateVisibility, this);
35809             this.on("panelremoved", this.validateVisibility, this);
35810         }
35811         if(this.autoScroll){
35812             this.bodyEl.setStyle("overflow", "auto");
35813         }else{
35814             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35815         }
35816         //if(c.titlebar !== false){
35817             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35818                 this.titleEl.hide();
35819             }else{
35820                 this.titleEl.show();
35821                 if(this.config.title){
35822                     this.titleTextEl.innerHTML = this.config.title;
35823                 }
35824             }
35825         //}
35826         if(this.config.collapsed){
35827             this.collapse(true);
35828         }
35829         if(this.config.hidden){
35830             this.hide();
35831         }
35832         
35833         if (this.unrendered_panels && this.unrendered_panels.length) {
35834             for (var i =0;i< this.unrendered_panels.length; i++) {
35835                 this.add(this.unrendered_panels[i]);
35836             }
35837             this.unrendered_panels = null;
35838             
35839         }
35840         
35841     },
35842     
35843     applyConfig : function(c)
35844     {
35845         /*
35846          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35847             var dh = Roo.DomHelper;
35848             if(c.titlebar !== false){
35849                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35850                 this.collapseBtn.on("click", this.collapse, this);
35851                 this.collapseBtn.enableDisplayMode();
35852                 /*
35853                 if(c.showPin === true || this.showPin){
35854                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35855                     this.stickBtn.enableDisplayMode();
35856                     this.stickBtn.on("click", this.expand, this);
35857                     this.stickBtn.hide();
35858                 }
35859                 
35860             }
35861             */
35862             /** This region's collapsed element
35863             * @type Roo.Element */
35864             /*
35865              *
35866             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35867                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35868             ]}, true);
35869             
35870             if(c.floatable !== false){
35871                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35872                this.collapsedEl.on("click", this.collapseClick, this);
35873             }
35874
35875             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35876                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35877                    id: "message", unselectable: "on", style:{"float":"left"}});
35878                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35879              }
35880             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35881             this.expandBtn.on("click", this.expand, this);
35882             
35883         }
35884         
35885         if(this.collapseBtn){
35886             this.collapseBtn.setVisible(c.collapsible == true);
35887         }
35888         
35889         this.cmargins = c.cmargins || this.cmargins ||
35890                          (this.position == "west" || this.position == "east" ?
35891                              {top: 0, left: 2, right:2, bottom: 0} :
35892                              {top: 2, left: 0, right:0, bottom: 2});
35893         */
35894         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35895         
35896         
35897         this.bottomTabs = c.tabPosition != "top";
35898         
35899         this.autoScroll = c.autoScroll || false;
35900         
35901         
35902        
35903         
35904         this.duration = c.duration || .30;
35905         this.slideDuration = c.slideDuration || .45;
35906         this.config = c;
35907        
35908     },
35909     /**
35910      * Returns true if this region is currently visible.
35911      * @return {Boolean}
35912      */
35913     isVisible : function(){
35914         return this.visible;
35915     },
35916
35917     /**
35918      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35919      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35920      */
35921     //setCollapsedTitle : function(title){
35922     //    title = title || "&#160;";
35923      //   if(this.collapsedTitleTextEl){
35924       //      this.collapsedTitleTextEl.innerHTML = title;
35925        // }
35926     //},
35927
35928     getBox : function(){
35929         var b;
35930       //  if(!this.collapsed){
35931             b = this.el.getBox(false, true);
35932        // }else{
35933           //  b = this.collapsedEl.getBox(false, true);
35934         //}
35935         return b;
35936     },
35937
35938     getMargins : function(){
35939         return this.margins;
35940         //return this.collapsed ? this.cmargins : this.margins;
35941     },
35942 /*
35943     highlight : function(){
35944         this.el.addClass("x-layout-panel-dragover");
35945     },
35946
35947     unhighlight : function(){
35948         this.el.removeClass("x-layout-panel-dragover");
35949     },
35950 */
35951     updateBox : function(box)
35952     {
35953         if (!this.bodyEl) {
35954             return; // not rendered yet..
35955         }
35956         
35957         this.box = box;
35958         if(!this.collapsed){
35959             this.el.dom.style.left = box.x + "px";
35960             this.el.dom.style.top = box.y + "px";
35961             this.updateBody(box.width, box.height);
35962         }else{
35963             this.collapsedEl.dom.style.left = box.x + "px";
35964             this.collapsedEl.dom.style.top = box.y + "px";
35965             this.collapsedEl.setSize(box.width, box.height);
35966         }
35967         if(this.tabs){
35968             this.tabs.autoSizeTabs();
35969         }
35970     },
35971
35972     updateBody : function(w, h)
35973     {
35974         if(w !== null){
35975             this.el.setWidth(w);
35976             w -= this.el.getBorderWidth("rl");
35977             if(this.config.adjustments){
35978                 w += this.config.adjustments[0];
35979             }
35980         }
35981         if(h !== null && h > 0){
35982             this.el.setHeight(h);
35983             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35984             h -= this.el.getBorderWidth("tb");
35985             if(this.config.adjustments){
35986                 h += this.config.adjustments[1];
35987             }
35988             this.bodyEl.setHeight(h);
35989             if(this.tabs){
35990                 h = this.tabs.syncHeight(h);
35991             }
35992         }
35993         if(this.panelSize){
35994             w = w !== null ? w : this.panelSize.width;
35995             h = h !== null ? h : this.panelSize.height;
35996         }
35997         if(this.activePanel){
35998             var el = this.activePanel.getEl();
35999             w = w !== null ? w : el.getWidth();
36000             h = h !== null ? h : el.getHeight();
36001             this.panelSize = {width: w, height: h};
36002             this.activePanel.setSize(w, h);
36003         }
36004         if(Roo.isIE && this.tabs){
36005             this.tabs.el.repaint();
36006         }
36007     },
36008
36009     /**
36010      * Returns the container element for this region.
36011      * @return {Roo.Element}
36012      */
36013     getEl : function(){
36014         return this.el;
36015     },
36016
36017     /**
36018      * Hides this region.
36019      */
36020     hide : function(){
36021         //if(!this.collapsed){
36022             this.el.dom.style.left = "-2000px";
36023             this.el.hide();
36024         //}else{
36025          //   this.collapsedEl.dom.style.left = "-2000px";
36026          //   this.collapsedEl.hide();
36027        // }
36028         this.visible = false;
36029         this.fireEvent("visibilitychange", this, false);
36030     },
36031
36032     /**
36033      * Shows this region if it was previously hidden.
36034      */
36035     show : function(){
36036         //if(!this.collapsed){
36037             this.el.show();
36038         //}else{
36039         //    this.collapsedEl.show();
36040        // }
36041         this.visible = true;
36042         this.fireEvent("visibilitychange", this, true);
36043     },
36044 /*
36045     closeClicked : function(){
36046         if(this.activePanel){
36047             this.remove(this.activePanel);
36048         }
36049     },
36050
36051     collapseClick : function(e){
36052         if(this.isSlid){
36053            e.stopPropagation();
36054            this.slideIn();
36055         }else{
36056            e.stopPropagation();
36057            this.slideOut();
36058         }
36059     },
36060 */
36061     /**
36062      * Collapses this region.
36063      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36064      */
36065     /*
36066     collapse : function(skipAnim, skipCheck = false){
36067         if(this.collapsed) {
36068             return;
36069         }
36070         
36071         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36072             
36073             this.collapsed = true;
36074             if(this.split){
36075                 this.split.el.hide();
36076             }
36077             if(this.config.animate && skipAnim !== true){
36078                 this.fireEvent("invalidated", this);
36079                 this.animateCollapse();
36080             }else{
36081                 this.el.setLocation(-20000,-20000);
36082                 this.el.hide();
36083                 this.collapsedEl.show();
36084                 this.fireEvent("collapsed", this);
36085                 this.fireEvent("invalidated", this);
36086             }
36087         }
36088         
36089     },
36090 */
36091     animateCollapse : function(){
36092         // overridden
36093     },
36094
36095     /**
36096      * Expands this region if it was previously collapsed.
36097      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36098      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36099      */
36100     /*
36101     expand : function(e, skipAnim){
36102         if(e) {
36103             e.stopPropagation();
36104         }
36105         if(!this.collapsed || this.el.hasActiveFx()) {
36106             return;
36107         }
36108         if(this.isSlid){
36109             this.afterSlideIn();
36110             skipAnim = true;
36111         }
36112         this.collapsed = false;
36113         if(this.config.animate && skipAnim !== true){
36114             this.animateExpand();
36115         }else{
36116             this.el.show();
36117             if(this.split){
36118                 this.split.el.show();
36119             }
36120             this.collapsedEl.setLocation(-2000,-2000);
36121             this.collapsedEl.hide();
36122             this.fireEvent("invalidated", this);
36123             this.fireEvent("expanded", this);
36124         }
36125     },
36126 */
36127     animateExpand : function(){
36128         // overridden
36129     },
36130
36131     initTabs : function()
36132     {
36133         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36134         
36135         var ts = new Roo.bootstrap.panel.Tabs({
36136                 el: this.bodyEl.dom,
36137                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36138                 disableTooltips: this.config.disableTabTips,
36139                 toolbar : this.config.toolbar
36140             });
36141         
36142         if(this.config.hideTabs){
36143             ts.stripWrap.setDisplayed(false);
36144         }
36145         this.tabs = ts;
36146         ts.resizeTabs = this.config.resizeTabs === true;
36147         ts.minTabWidth = this.config.minTabWidth || 40;
36148         ts.maxTabWidth = this.config.maxTabWidth || 250;
36149         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36150         ts.monitorResize = false;
36151         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36152         ts.bodyEl.addClass('roo-layout-tabs-body');
36153         this.panels.each(this.initPanelAsTab, this);
36154     },
36155
36156     initPanelAsTab : function(panel){
36157         var ti = this.tabs.addTab(
36158             panel.getEl().id,
36159             panel.getTitle(),
36160             null,
36161             this.config.closeOnTab && panel.isClosable(),
36162             panel.tpl
36163         );
36164         if(panel.tabTip !== undefined){
36165             ti.setTooltip(panel.tabTip);
36166         }
36167         ti.on("activate", function(){
36168               this.setActivePanel(panel);
36169         }, this);
36170         
36171         if(this.config.closeOnTab){
36172             ti.on("beforeclose", function(t, e){
36173                 e.cancel = true;
36174                 this.remove(panel);
36175             }, this);
36176         }
36177         
36178         panel.tabItem = ti;
36179         
36180         return ti;
36181     },
36182
36183     updatePanelTitle : function(panel, title)
36184     {
36185         if(this.activePanel == panel){
36186             this.updateTitle(title);
36187         }
36188         if(this.tabs){
36189             var ti = this.tabs.getTab(panel.getEl().id);
36190             ti.setText(title);
36191             if(panel.tabTip !== undefined){
36192                 ti.setTooltip(panel.tabTip);
36193             }
36194         }
36195     },
36196
36197     updateTitle : function(title){
36198         if(this.titleTextEl && !this.config.title){
36199             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36200         }
36201     },
36202
36203     setActivePanel : function(panel)
36204     {
36205         panel = this.getPanel(panel);
36206         if(this.activePanel && this.activePanel != panel){
36207             if(this.activePanel.setActiveState(false) === false){
36208                 return;
36209             }
36210         }
36211         this.activePanel = panel;
36212         panel.setActiveState(true);
36213         if(this.panelSize){
36214             panel.setSize(this.panelSize.width, this.panelSize.height);
36215         }
36216         if(this.closeBtn){
36217             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36218         }
36219         this.updateTitle(panel.getTitle());
36220         if(this.tabs){
36221             this.fireEvent("invalidated", this);
36222         }
36223         this.fireEvent("panelactivated", this, panel);
36224     },
36225
36226     /**
36227      * Shows the specified panel.
36228      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36229      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36230      */
36231     showPanel : function(panel)
36232     {
36233         panel = this.getPanel(panel);
36234         if(panel){
36235             if(this.tabs){
36236                 var tab = this.tabs.getTab(panel.getEl().id);
36237                 if(tab.isHidden()){
36238                     this.tabs.unhideTab(tab.id);
36239                 }
36240                 tab.activate();
36241             }else{
36242                 this.setActivePanel(panel);
36243             }
36244         }
36245         return panel;
36246     },
36247
36248     /**
36249      * Get the active panel for this region.
36250      * @return {Roo.ContentPanel} The active panel or null
36251      */
36252     getActivePanel : function(){
36253         return this.activePanel;
36254     },
36255
36256     validateVisibility : function(){
36257         if(this.panels.getCount() < 1){
36258             this.updateTitle("&#160;");
36259             this.closeBtn.hide();
36260             this.hide();
36261         }else{
36262             if(!this.isVisible()){
36263                 this.show();
36264             }
36265         }
36266     },
36267
36268     /**
36269      * Adds the passed ContentPanel(s) to this region.
36270      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36271      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36272      */
36273     add : function(panel)
36274     {
36275         if(arguments.length > 1){
36276             for(var i = 0, len = arguments.length; i < len; i++) {
36277                 this.add(arguments[i]);
36278             }
36279             return null;
36280         }
36281         
36282         // if we have not been rendered yet, then we can not really do much of this..
36283         if (!this.bodyEl) {
36284             this.unrendered_panels.push(panel);
36285             return panel;
36286         }
36287         
36288         
36289         
36290         
36291         if(this.hasPanel(panel)){
36292             this.showPanel(panel);
36293             return panel;
36294         }
36295         panel.setRegion(this);
36296         this.panels.add(panel);
36297        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36298             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36299             // and hide them... ???
36300             this.bodyEl.dom.appendChild(panel.getEl().dom);
36301             if(panel.background !== true){
36302                 this.setActivePanel(panel);
36303             }
36304             this.fireEvent("paneladded", this, panel);
36305             return panel;
36306         }
36307         */
36308         if(!this.tabs){
36309             this.initTabs();
36310         }else{
36311             this.initPanelAsTab(panel);
36312         }
36313         
36314         
36315         if(panel.background !== true){
36316             this.tabs.activate(panel.getEl().id);
36317         }
36318         this.fireEvent("paneladded", this, panel);
36319         return panel;
36320     },
36321
36322     /**
36323      * Hides the tab for the specified panel.
36324      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36325      */
36326     hidePanel : function(panel){
36327         if(this.tabs && (panel = this.getPanel(panel))){
36328             this.tabs.hideTab(panel.getEl().id);
36329         }
36330     },
36331
36332     /**
36333      * Unhides the tab for a previously hidden panel.
36334      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36335      */
36336     unhidePanel : function(panel){
36337         if(this.tabs && (panel = this.getPanel(panel))){
36338             this.tabs.unhideTab(panel.getEl().id);
36339         }
36340     },
36341
36342     clearPanels : function(){
36343         while(this.panels.getCount() > 0){
36344              this.remove(this.panels.first());
36345         }
36346     },
36347
36348     /**
36349      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36350      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36351      * @param {Boolean} preservePanel Overrides the config preservePanel option
36352      * @return {Roo.ContentPanel} The panel that was removed
36353      */
36354     remove : function(panel, preservePanel)
36355     {
36356         panel = this.getPanel(panel);
36357         if(!panel){
36358             return null;
36359         }
36360         var e = {};
36361         this.fireEvent("beforeremove", this, panel, e);
36362         if(e.cancel === true){
36363             return null;
36364         }
36365         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36366         var panelId = panel.getId();
36367         this.panels.removeKey(panelId);
36368         if(preservePanel){
36369             document.body.appendChild(panel.getEl().dom);
36370         }
36371         if(this.tabs){
36372             this.tabs.removeTab(panel.getEl().id);
36373         }else if (!preservePanel){
36374             this.bodyEl.dom.removeChild(panel.getEl().dom);
36375         }
36376         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36377             var p = this.panels.first();
36378             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36379             tempEl.appendChild(p.getEl().dom);
36380             this.bodyEl.update("");
36381             this.bodyEl.dom.appendChild(p.getEl().dom);
36382             tempEl = null;
36383             this.updateTitle(p.getTitle());
36384             this.tabs = null;
36385             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36386             this.setActivePanel(p);
36387         }
36388         panel.setRegion(null);
36389         if(this.activePanel == panel){
36390             this.activePanel = null;
36391         }
36392         if(this.config.autoDestroy !== false && preservePanel !== true){
36393             try{panel.destroy();}catch(e){}
36394         }
36395         this.fireEvent("panelremoved", this, panel);
36396         return panel;
36397     },
36398
36399     /**
36400      * Returns the TabPanel component used by this region
36401      * @return {Roo.TabPanel}
36402      */
36403     getTabs : function(){
36404         return this.tabs;
36405     },
36406
36407     createTool : function(parentEl, className){
36408         var btn = Roo.DomHelper.append(parentEl, {
36409             tag: "div",
36410             cls: "x-layout-tools-button",
36411             children: [ {
36412                 tag: "div",
36413                 cls: "roo-layout-tools-button-inner " + className,
36414                 html: "&#160;"
36415             }]
36416         }, true);
36417         btn.addClassOnOver("roo-layout-tools-button-over");
36418         return btn;
36419     }
36420 });/*
36421  * Based on:
36422  * Ext JS Library 1.1.1
36423  * Copyright(c) 2006-2007, Ext JS, LLC.
36424  *
36425  * Originally Released Under LGPL - original licence link has changed is not relivant.
36426  *
36427  * Fork - LGPL
36428  * <script type="text/javascript">
36429  */
36430  
36431
36432
36433 /**
36434  * @class Roo.SplitLayoutRegion
36435  * @extends Roo.LayoutRegion
36436  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36437  */
36438 Roo.bootstrap.layout.Split = function(config){
36439     this.cursor = config.cursor;
36440     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36441 };
36442
36443 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36444 {
36445     splitTip : "Drag to resize.",
36446     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36447     useSplitTips : false,
36448
36449     applyConfig : function(config){
36450         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36451     },
36452     
36453     onRender : function(ctr,pos) {
36454         
36455         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36456         if(!this.config.split){
36457             return;
36458         }
36459         if(!this.split){
36460             
36461             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36462                             tag: "div",
36463                             id: this.el.id + "-split",
36464                             cls: "roo-layout-split roo-layout-split-"+this.position,
36465                             html: "&#160;"
36466             });
36467             /** The SplitBar for this region 
36468             * @type Roo.SplitBar */
36469             // does not exist yet...
36470             Roo.log([this.position, this.orientation]);
36471             
36472             this.split = new Roo.bootstrap.SplitBar({
36473                 dragElement : splitEl,
36474                 resizingElement: this.el,
36475                 orientation : this.orientation
36476             });
36477             
36478             this.split.on("moved", this.onSplitMove, this);
36479             this.split.useShim = this.config.useShim === true;
36480             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36481             if(this.useSplitTips){
36482                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36483             }
36484             //if(config.collapsible){
36485             //    this.split.el.on("dblclick", this.collapse,  this);
36486             //}
36487         }
36488         if(typeof this.config.minSize != "undefined"){
36489             this.split.minSize = this.config.minSize;
36490         }
36491         if(typeof this.config.maxSize != "undefined"){
36492             this.split.maxSize = this.config.maxSize;
36493         }
36494         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36495             this.hideSplitter();
36496         }
36497         
36498     },
36499
36500     getHMaxSize : function(){
36501          var cmax = this.config.maxSize || 10000;
36502          var center = this.mgr.getRegion("center");
36503          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36504     },
36505
36506     getVMaxSize : function(){
36507          var cmax = this.config.maxSize || 10000;
36508          var center = this.mgr.getRegion("center");
36509          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36510     },
36511
36512     onSplitMove : function(split, newSize){
36513         this.fireEvent("resized", this, newSize);
36514     },
36515     
36516     /** 
36517      * Returns the {@link Roo.SplitBar} for this region.
36518      * @return {Roo.SplitBar}
36519      */
36520     getSplitBar : function(){
36521         return this.split;
36522     },
36523     
36524     hide : function(){
36525         this.hideSplitter();
36526         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36527     },
36528
36529     hideSplitter : function(){
36530         if(this.split){
36531             this.split.el.setLocation(-2000,-2000);
36532             this.split.el.hide();
36533         }
36534     },
36535
36536     show : function(){
36537         if(this.split){
36538             this.split.el.show();
36539         }
36540         Roo.bootstrap.layout.Split.superclass.show.call(this);
36541     },
36542     
36543     beforeSlide: function(){
36544         if(Roo.isGecko){// firefox overflow auto bug workaround
36545             this.bodyEl.clip();
36546             if(this.tabs) {
36547                 this.tabs.bodyEl.clip();
36548             }
36549             if(this.activePanel){
36550                 this.activePanel.getEl().clip();
36551                 
36552                 if(this.activePanel.beforeSlide){
36553                     this.activePanel.beforeSlide();
36554                 }
36555             }
36556         }
36557     },
36558     
36559     afterSlide : function(){
36560         if(Roo.isGecko){// firefox overflow auto bug workaround
36561             this.bodyEl.unclip();
36562             if(this.tabs) {
36563                 this.tabs.bodyEl.unclip();
36564             }
36565             if(this.activePanel){
36566                 this.activePanel.getEl().unclip();
36567                 if(this.activePanel.afterSlide){
36568                     this.activePanel.afterSlide();
36569                 }
36570             }
36571         }
36572     },
36573
36574     initAutoHide : function(){
36575         if(this.autoHide !== false){
36576             if(!this.autoHideHd){
36577                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36578                 this.autoHideHd = {
36579                     "mouseout": function(e){
36580                         if(!e.within(this.el, true)){
36581                             st.delay(500);
36582                         }
36583                     },
36584                     "mouseover" : function(e){
36585                         st.cancel();
36586                     },
36587                     scope : this
36588                 };
36589             }
36590             this.el.on(this.autoHideHd);
36591         }
36592     },
36593
36594     clearAutoHide : function(){
36595         if(this.autoHide !== false){
36596             this.el.un("mouseout", this.autoHideHd.mouseout);
36597             this.el.un("mouseover", this.autoHideHd.mouseover);
36598         }
36599     },
36600
36601     clearMonitor : function(){
36602         Roo.get(document).un("click", this.slideInIf, this);
36603     },
36604
36605     // these names are backwards but not changed for compat
36606     slideOut : function(){
36607         if(this.isSlid || this.el.hasActiveFx()){
36608             return;
36609         }
36610         this.isSlid = true;
36611         if(this.collapseBtn){
36612             this.collapseBtn.hide();
36613         }
36614         this.closeBtnState = this.closeBtn.getStyle('display');
36615         this.closeBtn.hide();
36616         if(this.stickBtn){
36617             this.stickBtn.show();
36618         }
36619         this.el.show();
36620         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36621         this.beforeSlide();
36622         this.el.setStyle("z-index", 10001);
36623         this.el.slideIn(this.getSlideAnchor(), {
36624             callback: function(){
36625                 this.afterSlide();
36626                 this.initAutoHide();
36627                 Roo.get(document).on("click", this.slideInIf, this);
36628                 this.fireEvent("slideshow", this);
36629             },
36630             scope: this,
36631             block: true
36632         });
36633     },
36634
36635     afterSlideIn : function(){
36636         this.clearAutoHide();
36637         this.isSlid = false;
36638         this.clearMonitor();
36639         this.el.setStyle("z-index", "");
36640         if(this.collapseBtn){
36641             this.collapseBtn.show();
36642         }
36643         this.closeBtn.setStyle('display', this.closeBtnState);
36644         if(this.stickBtn){
36645             this.stickBtn.hide();
36646         }
36647         this.fireEvent("slidehide", this);
36648     },
36649
36650     slideIn : function(cb){
36651         if(!this.isSlid || this.el.hasActiveFx()){
36652             Roo.callback(cb);
36653             return;
36654         }
36655         this.isSlid = false;
36656         this.beforeSlide();
36657         this.el.slideOut(this.getSlideAnchor(), {
36658             callback: function(){
36659                 this.el.setLeftTop(-10000, -10000);
36660                 this.afterSlide();
36661                 this.afterSlideIn();
36662                 Roo.callback(cb);
36663             },
36664             scope: this,
36665             block: true
36666         });
36667     },
36668     
36669     slideInIf : function(e){
36670         if(!e.within(this.el)){
36671             this.slideIn();
36672         }
36673     },
36674
36675     animateCollapse : function(){
36676         this.beforeSlide();
36677         this.el.setStyle("z-index", 20000);
36678         var anchor = this.getSlideAnchor();
36679         this.el.slideOut(anchor, {
36680             callback : function(){
36681                 this.el.setStyle("z-index", "");
36682                 this.collapsedEl.slideIn(anchor, {duration:.3});
36683                 this.afterSlide();
36684                 this.el.setLocation(-10000,-10000);
36685                 this.el.hide();
36686                 this.fireEvent("collapsed", this);
36687             },
36688             scope: this,
36689             block: true
36690         });
36691     },
36692
36693     animateExpand : function(){
36694         this.beforeSlide();
36695         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36696         this.el.setStyle("z-index", 20000);
36697         this.collapsedEl.hide({
36698             duration:.1
36699         });
36700         this.el.slideIn(this.getSlideAnchor(), {
36701             callback : function(){
36702                 this.el.setStyle("z-index", "");
36703                 this.afterSlide();
36704                 if(this.split){
36705                     this.split.el.show();
36706                 }
36707                 this.fireEvent("invalidated", this);
36708                 this.fireEvent("expanded", this);
36709             },
36710             scope: this,
36711             block: true
36712         });
36713     },
36714
36715     anchors : {
36716         "west" : "left",
36717         "east" : "right",
36718         "north" : "top",
36719         "south" : "bottom"
36720     },
36721
36722     sanchors : {
36723         "west" : "l",
36724         "east" : "r",
36725         "north" : "t",
36726         "south" : "b"
36727     },
36728
36729     canchors : {
36730         "west" : "tl-tr",
36731         "east" : "tr-tl",
36732         "north" : "tl-bl",
36733         "south" : "bl-tl"
36734     },
36735
36736     getAnchor : function(){
36737         return this.anchors[this.position];
36738     },
36739
36740     getCollapseAnchor : function(){
36741         return this.canchors[this.position];
36742     },
36743
36744     getSlideAnchor : function(){
36745         return this.sanchors[this.position];
36746     },
36747
36748     getAlignAdj : function(){
36749         var cm = this.cmargins;
36750         switch(this.position){
36751             case "west":
36752                 return [0, 0];
36753             break;
36754             case "east":
36755                 return [0, 0];
36756             break;
36757             case "north":
36758                 return [0, 0];
36759             break;
36760             case "south":
36761                 return [0, 0];
36762             break;
36763         }
36764     },
36765
36766     getExpandAdj : function(){
36767         var c = this.collapsedEl, cm = this.cmargins;
36768         switch(this.position){
36769             case "west":
36770                 return [-(cm.right+c.getWidth()+cm.left), 0];
36771             break;
36772             case "east":
36773                 return [cm.right+c.getWidth()+cm.left, 0];
36774             break;
36775             case "north":
36776                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36777             break;
36778             case "south":
36779                 return [0, cm.top+cm.bottom+c.getHeight()];
36780             break;
36781         }
36782     }
36783 });/*
36784  * Based on:
36785  * Ext JS Library 1.1.1
36786  * Copyright(c) 2006-2007, Ext JS, LLC.
36787  *
36788  * Originally Released Under LGPL - original licence link has changed is not relivant.
36789  *
36790  * Fork - LGPL
36791  * <script type="text/javascript">
36792  */
36793 /*
36794  * These classes are private internal classes
36795  */
36796 Roo.bootstrap.layout.Center = function(config){
36797     config.region = "center";
36798     Roo.bootstrap.layout.Region.call(this, config);
36799     this.visible = true;
36800     this.minWidth = config.minWidth || 20;
36801     this.minHeight = config.minHeight || 20;
36802 };
36803
36804 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36805     hide : function(){
36806         // center panel can't be hidden
36807     },
36808     
36809     show : function(){
36810         // center panel can't be hidden
36811     },
36812     
36813     getMinWidth: function(){
36814         return this.minWidth;
36815     },
36816     
36817     getMinHeight: function(){
36818         return this.minHeight;
36819     }
36820 });
36821
36822
36823
36824
36825  
36826
36827
36828
36829
36830
36831 Roo.bootstrap.layout.North = function(config)
36832 {
36833     config.region = 'north';
36834     config.cursor = 'n-resize';
36835     
36836     Roo.bootstrap.layout.Split.call(this, config);
36837     
36838     
36839     if(this.split){
36840         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36841         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36842         this.split.el.addClass("roo-layout-split-v");
36843     }
36844     var size = config.initialSize || config.height;
36845     if(typeof size != "undefined"){
36846         this.el.setHeight(size);
36847     }
36848 };
36849 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36850 {
36851     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36852     
36853     
36854     
36855     getBox : function(){
36856         if(this.collapsed){
36857             return this.collapsedEl.getBox();
36858         }
36859         var box = this.el.getBox();
36860         if(this.split){
36861             box.height += this.split.el.getHeight();
36862         }
36863         return box;
36864     },
36865     
36866     updateBox : function(box){
36867         if(this.split && !this.collapsed){
36868             box.height -= this.split.el.getHeight();
36869             this.split.el.setLeft(box.x);
36870             this.split.el.setTop(box.y+box.height);
36871             this.split.el.setWidth(box.width);
36872         }
36873         if(this.collapsed){
36874             this.updateBody(box.width, null);
36875         }
36876         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36877     }
36878 });
36879
36880
36881
36882
36883
36884 Roo.bootstrap.layout.South = function(config){
36885     config.region = 'south';
36886     config.cursor = 's-resize';
36887     Roo.bootstrap.layout.Split.call(this, config);
36888     if(this.split){
36889         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36890         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36891         this.split.el.addClass("roo-layout-split-v");
36892     }
36893     var size = config.initialSize || config.height;
36894     if(typeof size != "undefined"){
36895         this.el.setHeight(size);
36896     }
36897 };
36898
36899 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36900     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36901     getBox : function(){
36902         if(this.collapsed){
36903             return this.collapsedEl.getBox();
36904         }
36905         var box = this.el.getBox();
36906         if(this.split){
36907             var sh = this.split.el.getHeight();
36908             box.height += sh;
36909             box.y -= sh;
36910         }
36911         return box;
36912     },
36913     
36914     updateBox : function(box){
36915         if(this.split && !this.collapsed){
36916             var sh = this.split.el.getHeight();
36917             box.height -= sh;
36918             box.y += sh;
36919             this.split.el.setLeft(box.x);
36920             this.split.el.setTop(box.y-sh);
36921             this.split.el.setWidth(box.width);
36922         }
36923         if(this.collapsed){
36924             this.updateBody(box.width, null);
36925         }
36926         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36927     }
36928 });
36929
36930 Roo.bootstrap.layout.East = function(config){
36931     config.region = "east";
36932     config.cursor = "e-resize";
36933     Roo.bootstrap.layout.Split.call(this, config);
36934     if(this.split){
36935         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36936         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36937         this.split.el.addClass("roo-layout-split-h");
36938     }
36939     var size = config.initialSize || config.width;
36940     if(typeof size != "undefined"){
36941         this.el.setWidth(size);
36942     }
36943 };
36944 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36945     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36946     getBox : function(){
36947         if(this.collapsed){
36948             return this.collapsedEl.getBox();
36949         }
36950         var box = this.el.getBox();
36951         if(this.split){
36952             var sw = this.split.el.getWidth();
36953             box.width += sw;
36954             box.x -= sw;
36955         }
36956         return box;
36957     },
36958
36959     updateBox : function(box){
36960         if(this.split && !this.collapsed){
36961             var sw = this.split.el.getWidth();
36962             box.width -= sw;
36963             this.split.el.setLeft(box.x);
36964             this.split.el.setTop(box.y);
36965             this.split.el.setHeight(box.height);
36966             box.x += sw;
36967         }
36968         if(this.collapsed){
36969             this.updateBody(null, box.height);
36970         }
36971         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36972     }
36973 });
36974
36975 Roo.bootstrap.layout.West = function(config){
36976     config.region = "west";
36977     config.cursor = "w-resize";
36978     
36979     Roo.bootstrap.layout.Split.call(this, config);
36980     if(this.split){
36981         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36982         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36983         this.split.el.addClass("roo-layout-split-h");
36984     }
36985     
36986 };
36987 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36988     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36989     
36990     onRender: function(ctr, pos)
36991     {
36992         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36993         var size = this.config.initialSize || this.config.width;
36994         if(typeof size != "undefined"){
36995             this.el.setWidth(size);
36996         }
36997     },
36998     
36999     getBox : function(){
37000         if(this.collapsed){
37001             return this.collapsedEl.getBox();
37002         }
37003         var box = this.el.getBox();
37004         if(this.split){
37005             box.width += this.split.el.getWidth();
37006         }
37007         return box;
37008     },
37009     
37010     updateBox : function(box){
37011         if(this.split && !this.collapsed){
37012             var sw = this.split.el.getWidth();
37013             box.width -= sw;
37014             this.split.el.setLeft(box.x+box.width);
37015             this.split.el.setTop(box.y);
37016             this.split.el.setHeight(box.height);
37017         }
37018         if(this.collapsed){
37019             this.updateBody(null, box.height);
37020         }
37021         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37022     }
37023 });
37024 Roo.namespace("Roo.bootstrap.panel");/*
37025  * Based on:
37026  * Ext JS Library 1.1.1
37027  * Copyright(c) 2006-2007, Ext JS, LLC.
37028  *
37029  * Originally Released Under LGPL - original licence link has changed is not relivant.
37030  *
37031  * Fork - LGPL
37032  * <script type="text/javascript">
37033  */
37034 /**
37035  * @class Roo.ContentPanel
37036  * @extends Roo.util.Observable
37037  * A basic ContentPanel element.
37038  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37039  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37040  * @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
37041  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37042  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37043  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37044  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37045  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37046  * @cfg {String} title          The title for this panel
37047  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37048  * @cfg {String} url            Calls {@link #setUrl} with this value
37049  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37050  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37051  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37052  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37053  * @cfg {Boolean} badges render the badges
37054
37055  * @constructor
37056  * Create a new ContentPanel.
37057  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37058  * @param {String/Object} config A string to set only the title or a config object
37059  * @param {String} content (optional) Set the HTML content for this panel
37060  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37061  */
37062 Roo.bootstrap.panel.Content = function( config){
37063     
37064     this.tpl = config.tpl || false;
37065     
37066     var el = config.el;
37067     var content = config.content;
37068
37069     if(config.autoCreate){ // xtype is available if this is called from factory
37070         el = Roo.id();
37071     }
37072     this.el = Roo.get(el);
37073     if(!this.el && config && config.autoCreate){
37074         if(typeof config.autoCreate == "object"){
37075             if(!config.autoCreate.id){
37076                 config.autoCreate.id = config.id||el;
37077             }
37078             this.el = Roo.DomHelper.append(document.body,
37079                         config.autoCreate, true);
37080         }else{
37081             var elcfg =  {   tag: "div",
37082                             cls: "roo-layout-inactive-content",
37083                             id: config.id||el
37084                             };
37085             if (config.html) {
37086                 elcfg.html = config.html;
37087                 
37088             }
37089                         
37090             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37091         }
37092     } 
37093     this.closable = false;
37094     this.loaded = false;
37095     this.active = false;
37096    
37097       
37098     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37099         
37100         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37101         
37102         this.wrapEl = this.el; //this.el.wrap();
37103         var ti = [];
37104         if (config.toolbar.items) {
37105             ti = config.toolbar.items ;
37106             delete config.toolbar.items ;
37107         }
37108         
37109         var nitems = [];
37110         this.toolbar.render(this.wrapEl, 'before');
37111         for(var i =0;i < ti.length;i++) {
37112           //  Roo.log(['add child', items[i]]);
37113             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37114         }
37115         this.toolbar.items = nitems;
37116         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37117         delete config.toolbar;
37118         
37119     }
37120     /*
37121     // xtype created footer. - not sure if will work as we normally have to render first..
37122     if (this.footer && !this.footer.el && this.footer.xtype) {
37123         if (!this.wrapEl) {
37124             this.wrapEl = this.el.wrap();
37125         }
37126     
37127         this.footer.container = this.wrapEl.createChild();
37128          
37129         this.footer = Roo.factory(this.footer, Roo);
37130         
37131     }
37132     */
37133     
37134      if(typeof config == "string"){
37135         this.title = config;
37136     }else{
37137         Roo.apply(this, config);
37138     }
37139     
37140     if(this.resizeEl){
37141         this.resizeEl = Roo.get(this.resizeEl, true);
37142     }else{
37143         this.resizeEl = this.el;
37144     }
37145     // handle view.xtype
37146     
37147  
37148     
37149     
37150     this.addEvents({
37151         /**
37152          * @event activate
37153          * Fires when this panel is activated. 
37154          * @param {Roo.ContentPanel} this
37155          */
37156         "activate" : true,
37157         /**
37158          * @event deactivate
37159          * Fires when this panel is activated. 
37160          * @param {Roo.ContentPanel} this
37161          */
37162         "deactivate" : true,
37163
37164         /**
37165          * @event resize
37166          * Fires when this panel is resized if fitToFrame is true.
37167          * @param {Roo.ContentPanel} this
37168          * @param {Number} width The width after any component adjustments
37169          * @param {Number} height The height after any component adjustments
37170          */
37171         "resize" : true,
37172         
37173          /**
37174          * @event render
37175          * Fires when this tab is created
37176          * @param {Roo.ContentPanel} this
37177          */
37178         "render" : true
37179         
37180         
37181         
37182     });
37183     
37184
37185     
37186     
37187     if(this.autoScroll){
37188         this.resizeEl.setStyle("overflow", "auto");
37189     } else {
37190         // fix randome scrolling
37191         //this.el.on('scroll', function() {
37192         //    Roo.log('fix random scolling');
37193         //    this.scrollTo('top',0); 
37194         //});
37195     }
37196     content = content || this.content;
37197     if(content){
37198         this.setContent(content);
37199     }
37200     if(config && config.url){
37201         this.setUrl(this.url, this.params, this.loadOnce);
37202     }
37203     
37204     
37205     
37206     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37207     
37208     if (this.view && typeof(this.view.xtype) != 'undefined') {
37209         this.view.el = this.el.appendChild(document.createElement("div"));
37210         this.view = Roo.factory(this.view); 
37211         this.view.render  &&  this.view.render(false, '');  
37212     }
37213     
37214     
37215     this.fireEvent('render', this);
37216 };
37217
37218 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37219     
37220     tabTip : '',
37221     
37222     setRegion : function(region){
37223         this.region = region;
37224         this.setActiveClass(region && !this.background);
37225     },
37226     
37227     
37228     setActiveClass: function(state)
37229     {
37230         if(state){
37231            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37232            this.el.setStyle('position','relative');
37233         }else{
37234            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37235            this.el.setStyle('position', 'absolute');
37236         } 
37237     },
37238     
37239     /**
37240      * Returns the toolbar for this Panel if one was configured. 
37241      * @return {Roo.Toolbar} 
37242      */
37243     getToolbar : function(){
37244         return this.toolbar;
37245     },
37246     
37247     setActiveState : function(active)
37248     {
37249         this.active = active;
37250         this.setActiveClass(active);
37251         if(!active){
37252             if(this.fireEvent("deactivate", this) === false){
37253                 return false;
37254             }
37255             return true;
37256         }
37257         this.fireEvent("activate", this);
37258         return true;
37259     },
37260     /**
37261      * Updates this panel's element
37262      * @param {String} content The new content
37263      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37264     */
37265     setContent : function(content, loadScripts){
37266         this.el.update(content, loadScripts);
37267     },
37268
37269     ignoreResize : function(w, h){
37270         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37271             return true;
37272         }else{
37273             this.lastSize = {width: w, height: h};
37274             return false;
37275         }
37276     },
37277     /**
37278      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37279      * @return {Roo.UpdateManager} The UpdateManager
37280      */
37281     getUpdateManager : function(){
37282         return this.el.getUpdateManager();
37283     },
37284      /**
37285      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37286      * @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:
37287 <pre><code>
37288 panel.load({
37289     url: "your-url.php",
37290     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37291     callback: yourFunction,
37292     scope: yourObject, //(optional scope)
37293     discardUrl: false,
37294     nocache: false,
37295     text: "Loading...",
37296     timeout: 30,
37297     scripts: false
37298 });
37299 </code></pre>
37300      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37301      * 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.
37302      * @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}
37303      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37304      * @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.
37305      * @return {Roo.ContentPanel} this
37306      */
37307     load : function(){
37308         var um = this.el.getUpdateManager();
37309         um.update.apply(um, arguments);
37310         return this;
37311     },
37312
37313
37314     /**
37315      * 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.
37316      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37317      * @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)
37318      * @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)
37319      * @return {Roo.UpdateManager} The UpdateManager
37320      */
37321     setUrl : function(url, params, loadOnce){
37322         if(this.refreshDelegate){
37323             this.removeListener("activate", this.refreshDelegate);
37324         }
37325         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37326         this.on("activate", this.refreshDelegate);
37327         return this.el.getUpdateManager();
37328     },
37329     
37330     _handleRefresh : function(url, params, loadOnce){
37331         if(!loadOnce || !this.loaded){
37332             var updater = this.el.getUpdateManager();
37333             updater.update(url, params, this._setLoaded.createDelegate(this));
37334         }
37335     },
37336     
37337     _setLoaded : function(){
37338         this.loaded = true;
37339     }, 
37340     
37341     /**
37342      * Returns this panel's id
37343      * @return {String} 
37344      */
37345     getId : function(){
37346         return this.el.id;
37347     },
37348     
37349     /** 
37350      * Returns this panel's element - used by regiosn to add.
37351      * @return {Roo.Element} 
37352      */
37353     getEl : function(){
37354         return this.wrapEl || this.el;
37355     },
37356     
37357    
37358     
37359     adjustForComponents : function(width, height)
37360     {
37361         //Roo.log('adjustForComponents ');
37362         if(this.resizeEl != this.el){
37363             width -= this.el.getFrameWidth('lr');
37364             height -= this.el.getFrameWidth('tb');
37365         }
37366         if(this.toolbar){
37367             var te = this.toolbar.getEl();
37368             te.setWidth(width);
37369             height -= te.getHeight();
37370         }
37371         if(this.footer){
37372             var te = this.footer.getEl();
37373             te.setWidth(width);
37374             height -= te.getHeight();
37375         }
37376         
37377         
37378         if(this.adjustments){
37379             width += this.adjustments[0];
37380             height += this.adjustments[1];
37381         }
37382         return {"width": width, "height": height};
37383     },
37384     
37385     setSize : function(width, height){
37386         if(this.fitToFrame && !this.ignoreResize(width, height)){
37387             if(this.fitContainer && this.resizeEl != this.el){
37388                 this.el.setSize(width, height);
37389             }
37390             var size = this.adjustForComponents(width, height);
37391             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37392             this.fireEvent('resize', this, size.width, size.height);
37393         }
37394     },
37395     
37396     /**
37397      * Returns this panel's title
37398      * @return {String} 
37399      */
37400     getTitle : function(){
37401         
37402         if (typeof(this.title) != 'object') {
37403             return this.title;
37404         }
37405         
37406         var t = '';
37407         for (var k in this.title) {
37408             if (!this.title.hasOwnProperty(k)) {
37409                 continue;
37410             }
37411             
37412             if (k.indexOf('-') >= 0) {
37413                 var s = k.split('-');
37414                 for (var i = 0; i<s.length; i++) {
37415                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37416                 }
37417             } else {
37418                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37419             }
37420         }
37421         return t;
37422     },
37423     
37424     /**
37425      * Set this panel's title
37426      * @param {String} title
37427      */
37428     setTitle : function(title){
37429         this.title = title;
37430         if(this.region){
37431             this.region.updatePanelTitle(this, title);
37432         }
37433     },
37434     
37435     /**
37436      * Returns true is this panel was configured to be closable
37437      * @return {Boolean} 
37438      */
37439     isClosable : function(){
37440         return this.closable;
37441     },
37442     
37443     beforeSlide : function(){
37444         this.el.clip();
37445         this.resizeEl.clip();
37446     },
37447     
37448     afterSlide : function(){
37449         this.el.unclip();
37450         this.resizeEl.unclip();
37451     },
37452     
37453     /**
37454      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37455      *   Will fail silently if the {@link #setUrl} method has not been called.
37456      *   This does not activate the panel, just updates its content.
37457      */
37458     refresh : function(){
37459         if(this.refreshDelegate){
37460            this.loaded = false;
37461            this.refreshDelegate();
37462         }
37463     },
37464     
37465     /**
37466      * Destroys this panel
37467      */
37468     destroy : function(){
37469         this.el.removeAllListeners();
37470         var tempEl = document.createElement("span");
37471         tempEl.appendChild(this.el.dom);
37472         tempEl.innerHTML = "";
37473         this.el.remove();
37474         this.el = null;
37475     },
37476     
37477     /**
37478      * form - if the content panel contains a form - this is a reference to it.
37479      * @type {Roo.form.Form}
37480      */
37481     form : false,
37482     /**
37483      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37484      *    This contains a reference to it.
37485      * @type {Roo.View}
37486      */
37487     view : false,
37488     
37489       /**
37490      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37491      * <pre><code>
37492
37493 layout.addxtype({
37494        xtype : 'Form',
37495        items: [ .... ]
37496    }
37497 );
37498
37499 </code></pre>
37500      * @param {Object} cfg Xtype definition of item to add.
37501      */
37502     
37503     
37504     getChildContainer: function () {
37505         return this.getEl();
37506     }
37507     
37508     
37509     /*
37510         var  ret = new Roo.factory(cfg);
37511         return ret;
37512         
37513         
37514         // add form..
37515         if (cfg.xtype.match(/^Form$/)) {
37516             
37517             var el;
37518             //if (this.footer) {
37519             //    el = this.footer.container.insertSibling(false, 'before');
37520             //} else {
37521                 el = this.el.createChild();
37522             //}
37523
37524             this.form = new  Roo.form.Form(cfg);
37525             
37526             
37527             if ( this.form.allItems.length) {
37528                 this.form.render(el.dom);
37529             }
37530             return this.form;
37531         }
37532         // should only have one of theses..
37533         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37534             // views.. should not be just added - used named prop 'view''
37535             
37536             cfg.el = this.el.appendChild(document.createElement("div"));
37537             // factory?
37538             
37539             var ret = new Roo.factory(cfg);
37540              
37541              ret.render && ret.render(false, ''); // render blank..
37542             this.view = ret;
37543             return ret;
37544         }
37545         return false;
37546     }
37547     \*/
37548 });
37549  
37550 /**
37551  * @class Roo.bootstrap.panel.Grid
37552  * @extends Roo.bootstrap.panel.Content
37553  * @constructor
37554  * Create a new GridPanel.
37555  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37556  * @param {Object} config A the config object
37557   
37558  */
37559
37560
37561
37562 Roo.bootstrap.panel.Grid = function(config)
37563 {
37564     
37565       
37566     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37567         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37568
37569     config.el = this.wrapper;
37570     //this.el = this.wrapper;
37571     
37572       if (config.container) {
37573         // ctor'ed from a Border/panel.grid
37574         
37575         
37576         this.wrapper.setStyle("overflow", "hidden");
37577         this.wrapper.addClass('roo-grid-container');
37578
37579     }
37580     
37581     
37582     if(config.toolbar){
37583         var tool_el = this.wrapper.createChild();    
37584         this.toolbar = Roo.factory(config.toolbar);
37585         var ti = [];
37586         if (config.toolbar.items) {
37587             ti = config.toolbar.items ;
37588             delete config.toolbar.items ;
37589         }
37590         
37591         var nitems = [];
37592         this.toolbar.render(tool_el);
37593         for(var i =0;i < ti.length;i++) {
37594           //  Roo.log(['add child', items[i]]);
37595             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37596         }
37597         this.toolbar.items = nitems;
37598         
37599         delete config.toolbar;
37600     }
37601     
37602     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37603     config.grid.scrollBody = true;;
37604     config.grid.monitorWindowResize = false; // turn off autosizing
37605     config.grid.autoHeight = false;
37606     config.grid.autoWidth = false;
37607     
37608     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37609     
37610     if (config.background) {
37611         // render grid on panel activation (if panel background)
37612         this.on('activate', function(gp) {
37613             if (!gp.grid.rendered) {
37614                 gp.grid.render(this.wrapper);
37615                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37616             }
37617         });
37618             
37619     } else {
37620         this.grid.render(this.wrapper);
37621         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37622
37623     }
37624     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37625     // ??? needed ??? config.el = this.wrapper;
37626     
37627     
37628     
37629   
37630     // xtype created footer. - not sure if will work as we normally have to render first..
37631     if (this.footer && !this.footer.el && this.footer.xtype) {
37632         
37633         var ctr = this.grid.getView().getFooterPanel(true);
37634         this.footer.dataSource = this.grid.dataSource;
37635         this.footer = Roo.factory(this.footer, Roo);
37636         this.footer.render(ctr);
37637         
37638     }
37639     
37640     
37641     
37642     
37643      
37644 };
37645
37646 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37647     getId : function(){
37648         return this.grid.id;
37649     },
37650     
37651     /**
37652      * Returns the grid for this panel
37653      * @return {Roo.bootstrap.Table} 
37654      */
37655     getGrid : function(){
37656         return this.grid;    
37657     },
37658     
37659     setSize : function(width, height){
37660         if(!this.ignoreResize(width, height)){
37661             var grid = this.grid;
37662             var size = this.adjustForComponents(width, height);
37663             var gridel = grid.getGridEl();
37664             gridel.setSize(size.width, size.height);
37665             /*
37666             var thd = grid.getGridEl().select('thead',true).first();
37667             var tbd = grid.getGridEl().select('tbody', true).first();
37668             if (tbd) {
37669                 tbd.setSize(width, height - thd.getHeight());
37670             }
37671             */
37672             grid.autoSize();
37673         }
37674     },
37675      
37676     
37677     
37678     beforeSlide : function(){
37679         this.grid.getView().scroller.clip();
37680     },
37681     
37682     afterSlide : function(){
37683         this.grid.getView().scroller.unclip();
37684     },
37685     
37686     destroy : function(){
37687         this.grid.destroy();
37688         delete this.grid;
37689         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37690     }
37691 });
37692
37693 /**
37694  * @class Roo.bootstrap.panel.Nest
37695  * @extends Roo.bootstrap.panel.Content
37696  * @constructor
37697  * Create a new Panel, that can contain a layout.Border.
37698  * 
37699  * 
37700  * @param {Roo.BorderLayout} layout The layout for this panel
37701  * @param {String/Object} config A string to set only the title or a config object
37702  */
37703 Roo.bootstrap.panel.Nest = function(config)
37704 {
37705     // construct with only one argument..
37706     /* FIXME - implement nicer consturctors
37707     if (layout.layout) {
37708         config = layout;
37709         layout = config.layout;
37710         delete config.layout;
37711     }
37712     if (layout.xtype && !layout.getEl) {
37713         // then layout needs constructing..
37714         layout = Roo.factory(layout, Roo);
37715     }
37716     */
37717     
37718     config.el =  config.layout.getEl();
37719     
37720     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37721     
37722     config.layout.monitorWindowResize = false; // turn off autosizing
37723     this.layout = config.layout;
37724     this.layout.getEl().addClass("roo-layout-nested-layout");
37725     
37726     
37727     
37728     
37729 };
37730
37731 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37732
37733     setSize : function(width, height){
37734         if(!this.ignoreResize(width, height)){
37735             var size = this.adjustForComponents(width, height);
37736             var el = this.layout.getEl();
37737             if (size.height < 1) {
37738                 el.setWidth(size.width);   
37739             } else {
37740                 el.setSize(size.width, size.height);
37741             }
37742             var touch = el.dom.offsetWidth;
37743             this.layout.layout();
37744             // ie requires a double layout on the first pass
37745             if(Roo.isIE && !this.initialized){
37746                 this.initialized = true;
37747                 this.layout.layout();
37748             }
37749         }
37750     },
37751     
37752     // activate all subpanels if not currently active..
37753     
37754     setActiveState : function(active){
37755         this.active = active;
37756         this.setActiveClass(active);
37757         
37758         if(!active){
37759             this.fireEvent("deactivate", this);
37760             return;
37761         }
37762         
37763         this.fireEvent("activate", this);
37764         // not sure if this should happen before or after..
37765         if (!this.layout) {
37766             return; // should not happen..
37767         }
37768         var reg = false;
37769         for (var r in this.layout.regions) {
37770             reg = this.layout.getRegion(r);
37771             if (reg.getActivePanel()) {
37772                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37773                 reg.setActivePanel(reg.getActivePanel());
37774                 continue;
37775             }
37776             if (!reg.panels.length) {
37777                 continue;
37778             }
37779             reg.showPanel(reg.getPanel(0));
37780         }
37781         
37782         
37783         
37784         
37785     },
37786     
37787     /**
37788      * Returns the nested BorderLayout for this panel
37789      * @return {Roo.BorderLayout} 
37790      */
37791     getLayout : function(){
37792         return this.layout;
37793     },
37794     
37795      /**
37796      * Adds a xtype elements to the layout of the nested panel
37797      * <pre><code>
37798
37799 panel.addxtype({
37800        xtype : 'ContentPanel',
37801        region: 'west',
37802        items: [ .... ]
37803    }
37804 );
37805
37806 panel.addxtype({
37807         xtype : 'NestedLayoutPanel',
37808         region: 'west',
37809         layout: {
37810            center: { },
37811            west: { }   
37812         },
37813         items : [ ... list of content panels or nested layout panels.. ]
37814    }
37815 );
37816 </code></pre>
37817      * @param {Object} cfg Xtype definition of item to add.
37818      */
37819     addxtype : function(cfg) {
37820         return this.layout.addxtype(cfg);
37821     
37822     }
37823 });        /*
37824  * Based on:
37825  * Ext JS Library 1.1.1
37826  * Copyright(c) 2006-2007, Ext JS, LLC.
37827  *
37828  * Originally Released Under LGPL - original licence link has changed is not relivant.
37829  *
37830  * Fork - LGPL
37831  * <script type="text/javascript">
37832  */
37833 /**
37834  * @class Roo.TabPanel
37835  * @extends Roo.util.Observable
37836  * A lightweight tab container.
37837  * <br><br>
37838  * Usage:
37839  * <pre><code>
37840 // basic tabs 1, built from existing content
37841 var tabs = new Roo.TabPanel("tabs1");
37842 tabs.addTab("script", "View Script");
37843 tabs.addTab("markup", "View Markup");
37844 tabs.activate("script");
37845
37846 // more advanced tabs, built from javascript
37847 var jtabs = new Roo.TabPanel("jtabs");
37848 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37849
37850 // set up the UpdateManager
37851 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37852 var updater = tab2.getUpdateManager();
37853 updater.setDefaultUrl("ajax1.htm");
37854 tab2.on('activate', updater.refresh, updater, true);
37855
37856 // Use setUrl for Ajax loading
37857 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37858 tab3.setUrl("ajax2.htm", null, true);
37859
37860 // Disabled tab
37861 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37862 tab4.disable();
37863
37864 jtabs.activate("jtabs-1");
37865  * </code></pre>
37866  * @constructor
37867  * Create a new TabPanel.
37868  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37869  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37870  */
37871 Roo.bootstrap.panel.Tabs = function(config){
37872     /**
37873     * The container element for this TabPanel.
37874     * @type Roo.Element
37875     */
37876     this.el = Roo.get(config.el);
37877     delete config.el;
37878     if(config){
37879         if(typeof config == "boolean"){
37880             this.tabPosition = config ? "bottom" : "top";
37881         }else{
37882             Roo.apply(this, config);
37883         }
37884     }
37885     
37886     if(this.tabPosition == "bottom"){
37887         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37888         this.el.addClass("roo-tabs-bottom");
37889     }
37890     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37891     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37892     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37893     if(Roo.isIE){
37894         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37895     }
37896     if(this.tabPosition != "bottom"){
37897         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37898          * @type Roo.Element
37899          */
37900         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37901         this.el.addClass("roo-tabs-top");
37902     }
37903     this.items = [];
37904
37905     this.bodyEl.setStyle("position", "relative");
37906
37907     this.active = null;
37908     this.activateDelegate = this.activate.createDelegate(this);
37909
37910     this.addEvents({
37911         /**
37912          * @event tabchange
37913          * Fires when the active tab changes
37914          * @param {Roo.TabPanel} this
37915          * @param {Roo.TabPanelItem} activePanel The new active tab
37916          */
37917         "tabchange": true,
37918         /**
37919          * @event beforetabchange
37920          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37921          * @param {Roo.TabPanel} this
37922          * @param {Object} e Set cancel to true on this object to cancel the tab change
37923          * @param {Roo.TabPanelItem} tab The tab being changed to
37924          */
37925         "beforetabchange" : true
37926     });
37927
37928     Roo.EventManager.onWindowResize(this.onResize, this);
37929     this.cpad = this.el.getPadding("lr");
37930     this.hiddenCount = 0;
37931
37932
37933     // toolbar on the tabbar support...
37934     if (this.toolbar) {
37935         alert("no toolbar support yet");
37936         this.toolbar  = false;
37937         /*
37938         var tcfg = this.toolbar;
37939         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37940         this.toolbar = new Roo.Toolbar(tcfg);
37941         if (Roo.isSafari) {
37942             var tbl = tcfg.container.child('table', true);
37943             tbl.setAttribute('width', '100%');
37944         }
37945         */
37946         
37947     }
37948    
37949
37950
37951     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37952 };
37953
37954 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37955     /*
37956      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37957      */
37958     tabPosition : "top",
37959     /*
37960      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37961      */
37962     currentTabWidth : 0,
37963     /*
37964      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37965      */
37966     minTabWidth : 40,
37967     /*
37968      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37969      */
37970     maxTabWidth : 250,
37971     /*
37972      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37973      */
37974     preferredTabWidth : 175,
37975     /*
37976      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37977      */
37978     resizeTabs : false,
37979     /*
37980      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37981      */
37982     monitorResize : true,
37983     /*
37984      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37985      */
37986     toolbar : false,
37987
37988     /**
37989      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37990      * @param {String} id The id of the div to use <b>or create</b>
37991      * @param {String} text The text for the tab
37992      * @param {String} content (optional) Content to put in the TabPanelItem body
37993      * @param {Boolean} closable (optional) True to create a close icon on the tab
37994      * @return {Roo.TabPanelItem} The created TabPanelItem
37995      */
37996     addTab : function(id, text, content, closable, tpl)
37997     {
37998         var item = new Roo.bootstrap.panel.TabItem({
37999             panel: this,
38000             id : id,
38001             text : text,
38002             closable : closable,
38003             tpl : tpl
38004         });
38005         this.addTabItem(item);
38006         if(content){
38007             item.setContent(content);
38008         }
38009         return item;
38010     },
38011
38012     /**
38013      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38014      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38015      * @return {Roo.TabPanelItem}
38016      */
38017     getTab : function(id){
38018         return this.items[id];
38019     },
38020
38021     /**
38022      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38023      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38024      */
38025     hideTab : function(id){
38026         var t = this.items[id];
38027         if(!t.isHidden()){
38028            t.setHidden(true);
38029            this.hiddenCount++;
38030            this.autoSizeTabs();
38031         }
38032     },
38033
38034     /**
38035      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38036      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38037      */
38038     unhideTab : function(id){
38039         var t = this.items[id];
38040         if(t.isHidden()){
38041            t.setHidden(false);
38042            this.hiddenCount--;
38043            this.autoSizeTabs();
38044         }
38045     },
38046
38047     /**
38048      * Adds an existing {@link Roo.TabPanelItem}.
38049      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38050      */
38051     addTabItem : function(item){
38052         this.items[item.id] = item;
38053         this.items.push(item);
38054       //  if(this.resizeTabs){
38055     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38056   //         this.autoSizeTabs();
38057 //        }else{
38058 //            item.autoSize();
38059        // }
38060     },
38061
38062     /**
38063      * Removes a {@link Roo.TabPanelItem}.
38064      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38065      */
38066     removeTab : function(id){
38067         var items = this.items;
38068         var tab = items[id];
38069         if(!tab) { return; }
38070         var index = items.indexOf(tab);
38071         if(this.active == tab && items.length > 1){
38072             var newTab = this.getNextAvailable(index);
38073             if(newTab) {
38074                 newTab.activate();
38075             }
38076         }
38077         this.stripEl.dom.removeChild(tab.pnode.dom);
38078         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38079             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38080         }
38081         items.splice(index, 1);
38082         delete this.items[tab.id];
38083         tab.fireEvent("close", tab);
38084         tab.purgeListeners();
38085         this.autoSizeTabs();
38086     },
38087
38088     getNextAvailable : function(start){
38089         var items = this.items;
38090         var index = start;
38091         // look for a next tab that will slide over to
38092         // replace the one being removed
38093         while(index < items.length){
38094             var item = items[++index];
38095             if(item && !item.isHidden()){
38096                 return item;
38097             }
38098         }
38099         // if one isn't found select the previous tab (on the left)
38100         index = start;
38101         while(index >= 0){
38102             var item = items[--index];
38103             if(item && !item.isHidden()){
38104                 return item;
38105             }
38106         }
38107         return null;
38108     },
38109
38110     /**
38111      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38112      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38113      */
38114     disableTab : function(id){
38115         var tab = this.items[id];
38116         if(tab && this.active != tab){
38117             tab.disable();
38118         }
38119     },
38120
38121     /**
38122      * Enables a {@link Roo.TabPanelItem} that is disabled.
38123      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38124      */
38125     enableTab : function(id){
38126         var tab = this.items[id];
38127         tab.enable();
38128     },
38129
38130     /**
38131      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38132      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38133      * @return {Roo.TabPanelItem} The TabPanelItem.
38134      */
38135     activate : function(id){
38136         var tab = this.items[id];
38137         if(!tab){
38138             return null;
38139         }
38140         if(tab == this.active || tab.disabled){
38141             return tab;
38142         }
38143         var e = {};
38144         this.fireEvent("beforetabchange", this, e, tab);
38145         if(e.cancel !== true && !tab.disabled){
38146             if(this.active){
38147                 this.active.hide();
38148             }
38149             this.active = this.items[id];
38150             this.active.show();
38151             this.fireEvent("tabchange", this, this.active);
38152         }
38153         return tab;
38154     },
38155
38156     /**
38157      * Gets the active {@link Roo.TabPanelItem}.
38158      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38159      */
38160     getActiveTab : function(){
38161         return this.active;
38162     },
38163
38164     /**
38165      * Updates the tab body element to fit the height of the container element
38166      * for overflow scrolling
38167      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38168      */
38169     syncHeight : function(targetHeight){
38170         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38171         var bm = this.bodyEl.getMargins();
38172         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38173         this.bodyEl.setHeight(newHeight);
38174         return newHeight;
38175     },
38176
38177     onResize : function(){
38178         if(this.monitorResize){
38179             this.autoSizeTabs();
38180         }
38181     },
38182
38183     /**
38184      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38185      */
38186     beginUpdate : function(){
38187         this.updating = true;
38188     },
38189
38190     /**
38191      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38192      */
38193     endUpdate : function(){
38194         this.updating = false;
38195         this.autoSizeTabs();
38196     },
38197
38198     /**
38199      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38200      */
38201     autoSizeTabs : function(){
38202         var count = this.items.length;
38203         var vcount = count - this.hiddenCount;
38204         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38205             return;
38206         }
38207         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38208         var availWidth = Math.floor(w / vcount);
38209         var b = this.stripBody;
38210         if(b.getWidth() > w){
38211             var tabs = this.items;
38212             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38213             if(availWidth < this.minTabWidth){
38214                 /*if(!this.sleft){    // incomplete scrolling code
38215                     this.createScrollButtons();
38216                 }
38217                 this.showScroll();
38218                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38219             }
38220         }else{
38221             if(this.currentTabWidth < this.preferredTabWidth){
38222                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38223             }
38224         }
38225     },
38226
38227     /**
38228      * Returns the number of tabs in this TabPanel.
38229      * @return {Number}
38230      */
38231      getCount : function(){
38232          return this.items.length;
38233      },
38234
38235     /**
38236      * Resizes all the tabs to the passed width
38237      * @param {Number} The new width
38238      */
38239     setTabWidth : function(width){
38240         this.currentTabWidth = width;
38241         for(var i = 0, len = this.items.length; i < len; i++) {
38242                 if(!this.items[i].isHidden()) {
38243                 this.items[i].setWidth(width);
38244             }
38245         }
38246     },
38247
38248     /**
38249      * Destroys this TabPanel
38250      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38251      */
38252     destroy : function(removeEl){
38253         Roo.EventManager.removeResizeListener(this.onResize, this);
38254         for(var i = 0, len = this.items.length; i < len; i++){
38255             this.items[i].purgeListeners();
38256         }
38257         if(removeEl === true){
38258             this.el.update("");
38259             this.el.remove();
38260         }
38261     },
38262     
38263     createStrip : function(container)
38264     {
38265         var strip = document.createElement("nav");
38266         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38267         container.appendChild(strip);
38268         return strip;
38269     },
38270     
38271     createStripList : function(strip)
38272     {
38273         // div wrapper for retard IE
38274         // returns the "tr" element.
38275         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38276         //'<div class="x-tabs-strip-wrap">'+
38277           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38278           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38279         return strip.firstChild; //.firstChild.firstChild.firstChild;
38280     },
38281     createBody : function(container)
38282     {
38283         var body = document.createElement("div");
38284         Roo.id(body, "tab-body");
38285         //Roo.fly(body).addClass("x-tabs-body");
38286         Roo.fly(body).addClass("tab-content");
38287         container.appendChild(body);
38288         return body;
38289     },
38290     createItemBody :function(bodyEl, id){
38291         var body = Roo.getDom(id);
38292         if(!body){
38293             body = document.createElement("div");
38294             body.id = id;
38295         }
38296         //Roo.fly(body).addClass("x-tabs-item-body");
38297         Roo.fly(body).addClass("tab-pane");
38298          bodyEl.insertBefore(body, bodyEl.firstChild);
38299         return body;
38300     },
38301     /** @private */
38302     createStripElements :  function(stripEl, text, closable, tpl)
38303     {
38304         var td = document.createElement("li"); // was td..
38305         
38306         
38307         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38308         
38309         
38310         stripEl.appendChild(td);
38311         /*if(closable){
38312             td.className = "x-tabs-closable";
38313             if(!this.closeTpl){
38314                 this.closeTpl = new Roo.Template(
38315                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38316                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38317                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38318                 );
38319             }
38320             var el = this.closeTpl.overwrite(td, {"text": text});
38321             var close = el.getElementsByTagName("div")[0];
38322             var inner = el.getElementsByTagName("em")[0];
38323             return {"el": el, "close": close, "inner": inner};
38324         } else {
38325         */
38326         // not sure what this is..
38327 //            if(!this.tabTpl){
38328                 //this.tabTpl = new Roo.Template(
38329                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38330                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38331                 //);
38332 //                this.tabTpl = new Roo.Template(
38333 //                   '<a href="#">' +
38334 //                   '<span unselectable="on"' +
38335 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38336 //                            ' >{text}</span></a>'
38337 //                );
38338 //                
38339 //            }
38340
38341
38342             var template = tpl || this.tabTpl || false;
38343             
38344             if(!template){
38345                 
38346                 template = new Roo.Template(
38347                    '<a href="#">' +
38348                    '<span unselectable="on"' +
38349                             (this.disableTooltips ? '' : ' title="{text}"') +
38350                             ' >{text}</span></a>'
38351                 );
38352             }
38353             
38354             switch (typeof(template)) {
38355                 case 'object' :
38356                     break;
38357                 case 'string' :
38358                     template = new Roo.Template(template);
38359                     break;
38360                 default :
38361                     break;
38362             }
38363             
38364             var el = template.overwrite(td, {"text": text});
38365             
38366             var inner = el.getElementsByTagName("span")[0];
38367             
38368             return {"el": el, "inner": inner};
38369             
38370     }
38371         
38372     
38373 });
38374
38375 /**
38376  * @class Roo.TabPanelItem
38377  * @extends Roo.util.Observable
38378  * Represents an individual item (tab plus body) in a TabPanel.
38379  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38380  * @param {String} id The id of this TabPanelItem
38381  * @param {String} text The text for the tab of this TabPanelItem
38382  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38383  */
38384 Roo.bootstrap.panel.TabItem = function(config){
38385     /**
38386      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38387      * @type Roo.TabPanel
38388      */
38389     this.tabPanel = config.panel;
38390     /**
38391      * The id for this TabPanelItem
38392      * @type String
38393      */
38394     this.id = config.id;
38395     /** @private */
38396     this.disabled = false;
38397     /** @private */
38398     this.text = config.text;
38399     /** @private */
38400     this.loaded = false;
38401     this.closable = config.closable;
38402
38403     /**
38404      * The body element for this TabPanelItem.
38405      * @type Roo.Element
38406      */
38407     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38408     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38409     this.bodyEl.setStyle("display", "block");
38410     this.bodyEl.setStyle("zoom", "1");
38411     //this.hideAction();
38412
38413     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38414     /** @private */
38415     this.el = Roo.get(els.el);
38416     this.inner = Roo.get(els.inner, true);
38417     this.textEl = Roo.get(this.el.dom.firstChild, true);
38418     this.pnode = Roo.get(els.el.parentNode, true);
38419 //    this.el.on("mousedown", this.onTabMouseDown, this);
38420     this.el.on("click", this.onTabClick, this);
38421     /** @private */
38422     if(config.closable){
38423         var c = Roo.get(els.close, true);
38424         c.dom.title = this.closeText;
38425         c.addClassOnOver("close-over");
38426         c.on("click", this.closeClick, this);
38427      }
38428
38429     this.addEvents({
38430          /**
38431          * @event activate
38432          * Fires when this tab becomes the active tab.
38433          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38434          * @param {Roo.TabPanelItem} this
38435          */
38436         "activate": true,
38437         /**
38438          * @event beforeclose
38439          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38440          * @param {Roo.TabPanelItem} this
38441          * @param {Object} e Set cancel to true on this object to cancel the close.
38442          */
38443         "beforeclose": true,
38444         /**
38445          * @event close
38446          * Fires when this tab is closed.
38447          * @param {Roo.TabPanelItem} this
38448          */
38449          "close": true,
38450         /**
38451          * @event deactivate
38452          * Fires when this tab is no longer the active tab.
38453          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38454          * @param {Roo.TabPanelItem} this
38455          */
38456          "deactivate" : true
38457     });
38458     this.hidden = false;
38459
38460     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38461 };
38462
38463 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38464            {
38465     purgeListeners : function(){
38466        Roo.util.Observable.prototype.purgeListeners.call(this);
38467        this.el.removeAllListeners();
38468     },
38469     /**
38470      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38471      */
38472     show : function(){
38473         this.pnode.addClass("active");
38474         this.showAction();
38475         if(Roo.isOpera){
38476             this.tabPanel.stripWrap.repaint();
38477         }
38478         this.fireEvent("activate", this.tabPanel, this);
38479     },
38480
38481     /**
38482      * Returns true if this tab is the active tab.
38483      * @return {Boolean}
38484      */
38485     isActive : function(){
38486         return this.tabPanel.getActiveTab() == this;
38487     },
38488
38489     /**
38490      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38491      */
38492     hide : function(){
38493         this.pnode.removeClass("active");
38494         this.hideAction();
38495         this.fireEvent("deactivate", this.tabPanel, this);
38496     },
38497
38498     hideAction : function(){
38499         this.bodyEl.hide();
38500         this.bodyEl.setStyle("position", "absolute");
38501         this.bodyEl.setLeft("-20000px");
38502         this.bodyEl.setTop("-20000px");
38503     },
38504
38505     showAction : function(){
38506         this.bodyEl.setStyle("position", "relative");
38507         this.bodyEl.setTop("");
38508         this.bodyEl.setLeft("");
38509         this.bodyEl.show();
38510     },
38511
38512     /**
38513      * Set the tooltip for the tab.
38514      * @param {String} tooltip The tab's tooltip
38515      */
38516     setTooltip : function(text){
38517         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38518             this.textEl.dom.qtip = text;
38519             this.textEl.dom.removeAttribute('title');
38520         }else{
38521             this.textEl.dom.title = text;
38522         }
38523     },
38524
38525     onTabClick : function(e){
38526         e.preventDefault();
38527         this.tabPanel.activate(this.id);
38528     },
38529
38530     onTabMouseDown : function(e){
38531         e.preventDefault();
38532         this.tabPanel.activate(this.id);
38533     },
38534 /*
38535     getWidth : function(){
38536         return this.inner.getWidth();
38537     },
38538
38539     setWidth : function(width){
38540         var iwidth = width - this.pnode.getPadding("lr");
38541         this.inner.setWidth(iwidth);
38542         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38543         this.pnode.setWidth(width);
38544     },
38545 */
38546     /**
38547      * Show or hide the tab
38548      * @param {Boolean} hidden True to hide or false to show.
38549      */
38550     setHidden : function(hidden){
38551         this.hidden = hidden;
38552         this.pnode.setStyle("display", hidden ? "none" : "");
38553     },
38554
38555     /**
38556      * Returns true if this tab is "hidden"
38557      * @return {Boolean}
38558      */
38559     isHidden : function(){
38560         return this.hidden;
38561     },
38562
38563     /**
38564      * Returns the text for this tab
38565      * @return {String}
38566      */
38567     getText : function(){
38568         return this.text;
38569     },
38570     /*
38571     autoSize : function(){
38572         //this.el.beginMeasure();
38573         this.textEl.setWidth(1);
38574         /*
38575          *  #2804 [new] Tabs in Roojs
38576          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38577          */
38578         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38579         //this.el.endMeasure();
38580     //},
38581
38582     /**
38583      * Sets the text for the tab (Note: this also sets the tooltip text)
38584      * @param {String} text The tab's text and tooltip
38585      */
38586     setText : function(text){
38587         this.text = text;
38588         this.textEl.update(text);
38589         this.setTooltip(text);
38590         //if(!this.tabPanel.resizeTabs){
38591         //    this.autoSize();
38592         //}
38593     },
38594     /**
38595      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38596      */
38597     activate : function(){
38598         this.tabPanel.activate(this.id);
38599     },
38600
38601     /**
38602      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38603      */
38604     disable : function(){
38605         if(this.tabPanel.active != this){
38606             this.disabled = true;
38607             this.pnode.addClass("disabled");
38608         }
38609     },
38610
38611     /**
38612      * Enables this TabPanelItem if it was previously disabled.
38613      */
38614     enable : function(){
38615         this.disabled = false;
38616         this.pnode.removeClass("disabled");
38617     },
38618
38619     /**
38620      * Sets the content for this TabPanelItem.
38621      * @param {String} content The content
38622      * @param {Boolean} loadScripts true to look for and load scripts
38623      */
38624     setContent : function(content, loadScripts){
38625         this.bodyEl.update(content, loadScripts);
38626     },
38627
38628     /**
38629      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38630      * @return {Roo.UpdateManager} The UpdateManager
38631      */
38632     getUpdateManager : function(){
38633         return this.bodyEl.getUpdateManager();
38634     },
38635
38636     /**
38637      * Set a URL to be used to load the content for this TabPanelItem.
38638      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38639      * @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)
38640      * @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)
38641      * @return {Roo.UpdateManager} The UpdateManager
38642      */
38643     setUrl : function(url, params, loadOnce){
38644         if(this.refreshDelegate){
38645             this.un('activate', this.refreshDelegate);
38646         }
38647         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38648         this.on("activate", this.refreshDelegate);
38649         return this.bodyEl.getUpdateManager();
38650     },
38651
38652     /** @private */
38653     _handleRefresh : function(url, params, loadOnce){
38654         if(!loadOnce || !this.loaded){
38655             var updater = this.bodyEl.getUpdateManager();
38656             updater.update(url, params, this._setLoaded.createDelegate(this));
38657         }
38658     },
38659
38660     /**
38661      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38662      *   Will fail silently if the setUrl method has not been called.
38663      *   This does not activate the panel, just updates its content.
38664      */
38665     refresh : function(){
38666         if(this.refreshDelegate){
38667            this.loaded = false;
38668            this.refreshDelegate();
38669         }
38670     },
38671
38672     /** @private */
38673     _setLoaded : function(){
38674         this.loaded = true;
38675     },
38676
38677     /** @private */
38678     closeClick : function(e){
38679         var o = {};
38680         e.stopEvent();
38681         this.fireEvent("beforeclose", this, o);
38682         if(o.cancel !== true){
38683             this.tabPanel.removeTab(this.id);
38684         }
38685     },
38686     /**
38687      * The text displayed in the tooltip for the close icon.
38688      * @type String
38689      */
38690     closeText : "Close this tab"
38691 });
38692 /**
38693 *    This script refer to:
38694 *    Title: International Telephone Input
38695 *    Author: Jack O'Connor
38696 *    Code version:  v12.1.12
38697 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38698 **/
38699
38700 Roo.bootstrap.PhoneInputData = function() {
38701     var d = [
38702       [
38703         "Afghanistan (‫افغانستان‬‎)",
38704         "af",
38705         "93"
38706       ],
38707       [
38708         "Albania (Shqipëri)",
38709         "al",
38710         "355"
38711       ],
38712       [
38713         "Algeria (‫الجزائر‬‎)",
38714         "dz",
38715         "213"
38716       ],
38717       [
38718         "American Samoa",
38719         "as",
38720         "1684"
38721       ],
38722       [
38723         "Andorra",
38724         "ad",
38725         "376"
38726       ],
38727       [
38728         "Angola",
38729         "ao",
38730         "244"
38731       ],
38732       [
38733         "Anguilla",
38734         "ai",
38735         "1264"
38736       ],
38737       [
38738         "Antigua and Barbuda",
38739         "ag",
38740         "1268"
38741       ],
38742       [
38743         "Argentina",
38744         "ar",
38745         "54"
38746       ],
38747       [
38748         "Armenia (Հայաստան)",
38749         "am",
38750         "374"
38751       ],
38752       [
38753         "Aruba",
38754         "aw",
38755         "297"
38756       ],
38757       [
38758         "Australia",
38759         "au",
38760         "61",
38761         0
38762       ],
38763       [
38764         "Austria (Österreich)",
38765         "at",
38766         "43"
38767       ],
38768       [
38769         "Azerbaijan (Azərbaycan)",
38770         "az",
38771         "994"
38772       ],
38773       [
38774         "Bahamas",
38775         "bs",
38776         "1242"
38777       ],
38778       [
38779         "Bahrain (‫البحرين‬‎)",
38780         "bh",
38781         "973"
38782       ],
38783       [
38784         "Bangladesh (বাংলাদেশ)",
38785         "bd",
38786         "880"
38787       ],
38788       [
38789         "Barbados",
38790         "bb",
38791         "1246"
38792       ],
38793       [
38794         "Belarus (Беларусь)",
38795         "by",
38796         "375"
38797       ],
38798       [
38799         "Belgium (België)",
38800         "be",
38801         "32"
38802       ],
38803       [
38804         "Belize",
38805         "bz",
38806         "501"
38807       ],
38808       [
38809         "Benin (Bénin)",
38810         "bj",
38811         "229"
38812       ],
38813       [
38814         "Bermuda",
38815         "bm",
38816         "1441"
38817       ],
38818       [
38819         "Bhutan (འབྲུག)",
38820         "bt",
38821         "975"
38822       ],
38823       [
38824         "Bolivia",
38825         "bo",
38826         "591"
38827       ],
38828       [
38829         "Bosnia and Herzegovina (Босна и Херцеговина)",
38830         "ba",
38831         "387"
38832       ],
38833       [
38834         "Botswana",
38835         "bw",
38836         "267"
38837       ],
38838       [
38839         "Brazil (Brasil)",
38840         "br",
38841         "55"
38842       ],
38843       [
38844         "British Indian Ocean Territory",
38845         "io",
38846         "246"
38847       ],
38848       [
38849         "British Virgin Islands",
38850         "vg",
38851         "1284"
38852       ],
38853       [
38854         "Brunei",
38855         "bn",
38856         "673"
38857       ],
38858       [
38859         "Bulgaria (България)",
38860         "bg",
38861         "359"
38862       ],
38863       [
38864         "Burkina Faso",
38865         "bf",
38866         "226"
38867       ],
38868       [
38869         "Burundi (Uburundi)",
38870         "bi",
38871         "257"
38872       ],
38873       [
38874         "Cambodia (កម្ពុជា)",
38875         "kh",
38876         "855"
38877       ],
38878       [
38879         "Cameroon (Cameroun)",
38880         "cm",
38881         "237"
38882       ],
38883       [
38884         "Canada",
38885         "ca",
38886         "1",
38887         1,
38888         ["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"]
38889       ],
38890       [
38891         "Cape Verde (Kabu Verdi)",
38892         "cv",
38893         "238"
38894       ],
38895       [
38896         "Caribbean Netherlands",
38897         "bq",
38898         "599",
38899         1
38900       ],
38901       [
38902         "Cayman Islands",
38903         "ky",
38904         "1345"
38905       ],
38906       [
38907         "Central African Republic (République centrafricaine)",
38908         "cf",
38909         "236"
38910       ],
38911       [
38912         "Chad (Tchad)",
38913         "td",
38914         "235"
38915       ],
38916       [
38917         "Chile",
38918         "cl",
38919         "56"
38920       ],
38921       [
38922         "China (中国)",
38923         "cn",
38924         "86"
38925       ],
38926       [
38927         "Christmas Island",
38928         "cx",
38929         "61",
38930         2
38931       ],
38932       [
38933         "Cocos (Keeling) Islands",
38934         "cc",
38935         "61",
38936         1
38937       ],
38938       [
38939         "Colombia",
38940         "co",
38941         "57"
38942       ],
38943       [
38944         "Comoros (‫جزر القمر‬‎)",
38945         "km",
38946         "269"
38947       ],
38948       [
38949         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38950         "cd",
38951         "243"
38952       ],
38953       [
38954         "Congo (Republic) (Congo-Brazzaville)",
38955         "cg",
38956         "242"
38957       ],
38958       [
38959         "Cook Islands",
38960         "ck",
38961         "682"
38962       ],
38963       [
38964         "Costa Rica",
38965         "cr",
38966         "506"
38967       ],
38968       [
38969         "Côte d’Ivoire",
38970         "ci",
38971         "225"
38972       ],
38973       [
38974         "Croatia (Hrvatska)",
38975         "hr",
38976         "385"
38977       ],
38978       [
38979         "Cuba",
38980         "cu",
38981         "53"
38982       ],
38983       [
38984         "Curaçao",
38985         "cw",
38986         "599",
38987         0
38988       ],
38989       [
38990         "Cyprus (Κύπρος)",
38991         "cy",
38992         "357"
38993       ],
38994       [
38995         "Czech Republic (Česká republika)",
38996         "cz",
38997         "420"
38998       ],
38999       [
39000         "Denmark (Danmark)",
39001         "dk",
39002         "45"
39003       ],
39004       [
39005         "Djibouti",
39006         "dj",
39007         "253"
39008       ],
39009       [
39010         "Dominica",
39011         "dm",
39012         "1767"
39013       ],
39014       [
39015         "Dominican Republic (República Dominicana)",
39016         "do",
39017         "1",
39018         2,
39019         ["809", "829", "849"]
39020       ],
39021       [
39022         "Ecuador",
39023         "ec",
39024         "593"
39025       ],
39026       [
39027         "Egypt (‫مصر‬‎)",
39028         "eg",
39029         "20"
39030       ],
39031       [
39032         "El Salvador",
39033         "sv",
39034         "503"
39035       ],
39036       [
39037         "Equatorial Guinea (Guinea Ecuatorial)",
39038         "gq",
39039         "240"
39040       ],
39041       [
39042         "Eritrea",
39043         "er",
39044         "291"
39045       ],
39046       [
39047         "Estonia (Eesti)",
39048         "ee",
39049         "372"
39050       ],
39051       [
39052         "Ethiopia",
39053         "et",
39054         "251"
39055       ],
39056       [
39057         "Falkland Islands (Islas Malvinas)",
39058         "fk",
39059         "500"
39060       ],
39061       [
39062         "Faroe Islands (Føroyar)",
39063         "fo",
39064         "298"
39065       ],
39066       [
39067         "Fiji",
39068         "fj",
39069         "679"
39070       ],
39071       [
39072         "Finland (Suomi)",
39073         "fi",
39074         "358",
39075         0
39076       ],
39077       [
39078         "France",
39079         "fr",
39080         "33"
39081       ],
39082       [
39083         "French Guiana (Guyane française)",
39084         "gf",
39085         "594"
39086       ],
39087       [
39088         "French Polynesia (Polynésie française)",
39089         "pf",
39090         "689"
39091       ],
39092       [
39093         "Gabon",
39094         "ga",
39095         "241"
39096       ],
39097       [
39098         "Gambia",
39099         "gm",
39100         "220"
39101       ],
39102       [
39103         "Georgia (საქართველო)",
39104         "ge",
39105         "995"
39106       ],
39107       [
39108         "Germany (Deutschland)",
39109         "de",
39110         "49"
39111       ],
39112       [
39113         "Ghana (Gaana)",
39114         "gh",
39115         "233"
39116       ],
39117       [
39118         "Gibraltar",
39119         "gi",
39120         "350"
39121       ],
39122       [
39123         "Greece (Ελλάδα)",
39124         "gr",
39125         "30"
39126       ],
39127       [
39128         "Greenland (Kalaallit Nunaat)",
39129         "gl",
39130         "299"
39131       ],
39132       [
39133         "Grenada",
39134         "gd",
39135         "1473"
39136       ],
39137       [
39138         "Guadeloupe",
39139         "gp",
39140         "590",
39141         0
39142       ],
39143       [
39144         "Guam",
39145         "gu",
39146         "1671"
39147       ],
39148       [
39149         "Guatemala",
39150         "gt",
39151         "502"
39152       ],
39153       [
39154         "Guernsey",
39155         "gg",
39156         "44",
39157         1
39158       ],
39159       [
39160         "Guinea (Guinée)",
39161         "gn",
39162         "224"
39163       ],
39164       [
39165         "Guinea-Bissau (Guiné Bissau)",
39166         "gw",
39167         "245"
39168       ],
39169       [
39170         "Guyana",
39171         "gy",
39172         "592"
39173       ],
39174       [
39175         "Haiti",
39176         "ht",
39177         "509"
39178       ],
39179       [
39180         "Honduras",
39181         "hn",
39182         "504"
39183       ],
39184       [
39185         "Hong Kong (香港)",
39186         "hk",
39187         "852"
39188       ],
39189       [
39190         "Hungary (Magyarország)",
39191         "hu",
39192         "36"
39193       ],
39194       [
39195         "Iceland (Ísland)",
39196         "is",
39197         "354"
39198       ],
39199       [
39200         "India (भारत)",
39201         "in",
39202         "91"
39203       ],
39204       [
39205         "Indonesia",
39206         "id",
39207         "62"
39208       ],
39209       [
39210         "Iran (‫ایران‬‎)",
39211         "ir",
39212         "98"
39213       ],
39214       [
39215         "Iraq (‫العراق‬‎)",
39216         "iq",
39217         "964"
39218       ],
39219       [
39220         "Ireland",
39221         "ie",
39222         "353"
39223       ],
39224       [
39225         "Isle of Man",
39226         "im",
39227         "44",
39228         2
39229       ],
39230       [
39231         "Israel (‫ישראל‬‎)",
39232         "il",
39233         "972"
39234       ],
39235       [
39236         "Italy (Italia)",
39237         "it",
39238         "39",
39239         0
39240       ],
39241       [
39242         "Jamaica",
39243         "jm",
39244         "1876"
39245       ],
39246       [
39247         "Japan (日本)",
39248         "jp",
39249         "81"
39250       ],
39251       [
39252         "Jersey",
39253         "je",
39254         "44",
39255         3
39256       ],
39257       [
39258         "Jordan (‫الأردن‬‎)",
39259         "jo",
39260         "962"
39261       ],
39262       [
39263         "Kazakhstan (Казахстан)",
39264         "kz",
39265         "7",
39266         1
39267       ],
39268       [
39269         "Kenya",
39270         "ke",
39271         "254"
39272       ],
39273       [
39274         "Kiribati",
39275         "ki",
39276         "686"
39277       ],
39278       [
39279         "Kosovo",
39280         "xk",
39281         "383"
39282       ],
39283       [
39284         "Kuwait (‫الكويت‬‎)",
39285         "kw",
39286         "965"
39287       ],
39288       [
39289         "Kyrgyzstan (Кыргызстан)",
39290         "kg",
39291         "996"
39292       ],
39293       [
39294         "Laos (ລາວ)",
39295         "la",
39296         "856"
39297       ],
39298       [
39299         "Latvia (Latvija)",
39300         "lv",
39301         "371"
39302       ],
39303       [
39304         "Lebanon (‫لبنان‬‎)",
39305         "lb",
39306         "961"
39307       ],
39308       [
39309         "Lesotho",
39310         "ls",
39311         "266"
39312       ],
39313       [
39314         "Liberia",
39315         "lr",
39316         "231"
39317       ],
39318       [
39319         "Libya (‫ليبيا‬‎)",
39320         "ly",
39321         "218"
39322       ],
39323       [
39324         "Liechtenstein",
39325         "li",
39326         "423"
39327       ],
39328       [
39329         "Lithuania (Lietuva)",
39330         "lt",
39331         "370"
39332       ],
39333       [
39334         "Luxembourg",
39335         "lu",
39336         "352"
39337       ],
39338       [
39339         "Macau (澳門)",
39340         "mo",
39341         "853"
39342       ],
39343       [
39344         "Macedonia (FYROM) (Македонија)",
39345         "mk",
39346         "389"
39347       ],
39348       [
39349         "Madagascar (Madagasikara)",
39350         "mg",
39351         "261"
39352       ],
39353       [
39354         "Malawi",
39355         "mw",
39356         "265"
39357       ],
39358       [
39359         "Malaysia",
39360         "my",
39361         "60"
39362       ],
39363       [
39364         "Maldives",
39365         "mv",
39366         "960"
39367       ],
39368       [
39369         "Mali",
39370         "ml",
39371         "223"
39372       ],
39373       [
39374         "Malta",
39375         "mt",
39376         "356"
39377       ],
39378       [
39379         "Marshall Islands",
39380         "mh",
39381         "692"
39382       ],
39383       [
39384         "Martinique",
39385         "mq",
39386         "596"
39387       ],
39388       [
39389         "Mauritania (‫موريتانيا‬‎)",
39390         "mr",
39391         "222"
39392       ],
39393       [
39394         "Mauritius (Moris)",
39395         "mu",
39396         "230"
39397       ],
39398       [
39399         "Mayotte",
39400         "yt",
39401         "262",
39402         1
39403       ],
39404       [
39405         "Mexico (México)",
39406         "mx",
39407         "52"
39408       ],
39409       [
39410         "Micronesia",
39411         "fm",
39412         "691"
39413       ],
39414       [
39415         "Moldova (Republica Moldova)",
39416         "md",
39417         "373"
39418       ],
39419       [
39420         "Monaco",
39421         "mc",
39422         "377"
39423       ],
39424       [
39425         "Mongolia (Монгол)",
39426         "mn",
39427         "976"
39428       ],
39429       [
39430         "Montenegro (Crna Gora)",
39431         "me",
39432         "382"
39433       ],
39434       [
39435         "Montserrat",
39436         "ms",
39437         "1664"
39438       ],
39439       [
39440         "Morocco (‫المغرب‬‎)",
39441         "ma",
39442         "212",
39443         0
39444       ],
39445       [
39446         "Mozambique (Moçambique)",
39447         "mz",
39448         "258"
39449       ],
39450       [
39451         "Myanmar (Burma) (မြန်မာ)",
39452         "mm",
39453         "95"
39454       ],
39455       [
39456         "Namibia (Namibië)",
39457         "na",
39458         "264"
39459       ],
39460       [
39461         "Nauru",
39462         "nr",
39463         "674"
39464       ],
39465       [
39466         "Nepal (नेपाल)",
39467         "np",
39468         "977"
39469       ],
39470       [
39471         "Netherlands (Nederland)",
39472         "nl",
39473         "31"
39474       ],
39475       [
39476         "New Caledonia (Nouvelle-Calédonie)",
39477         "nc",
39478         "687"
39479       ],
39480       [
39481         "New Zealand",
39482         "nz",
39483         "64"
39484       ],
39485       [
39486         "Nicaragua",
39487         "ni",
39488         "505"
39489       ],
39490       [
39491         "Niger (Nijar)",
39492         "ne",
39493         "227"
39494       ],
39495       [
39496         "Nigeria",
39497         "ng",
39498         "234"
39499       ],
39500       [
39501         "Niue",
39502         "nu",
39503         "683"
39504       ],
39505       [
39506         "Norfolk Island",
39507         "nf",
39508         "672"
39509       ],
39510       [
39511         "North Korea (조선 민주주의 인민 공화국)",
39512         "kp",
39513         "850"
39514       ],
39515       [
39516         "Northern Mariana Islands",
39517         "mp",
39518         "1670"
39519       ],
39520       [
39521         "Norway (Norge)",
39522         "no",
39523         "47",
39524         0
39525       ],
39526       [
39527         "Oman (‫عُمان‬‎)",
39528         "om",
39529         "968"
39530       ],
39531       [
39532         "Pakistan (‫پاکستان‬‎)",
39533         "pk",
39534         "92"
39535       ],
39536       [
39537         "Palau",
39538         "pw",
39539         "680"
39540       ],
39541       [
39542         "Palestine (‫فلسطين‬‎)",
39543         "ps",
39544         "970"
39545       ],
39546       [
39547         "Panama (Panamá)",
39548         "pa",
39549         "507"
39550       ],
39551       [
39552         "Papua New Guinea",
39553         "pg",
39554         "675"
39555       ],
39556       [
39557         "Paraguay",
39558         "py",
39559         "595"
39560       ],
39561       [
39562         "Peru (Perú)",
39563         "pe",
39564         "51"
39565       ],
39566       [
39567         "Philippines",
39568         "ph",
39569         "63"
39570       ],
39571       [
39572         "Poland (Polska)",
39573         "pl",
39574         "48"
39575       ],
39576       [
39577         "Portugal",
39578         "pt",
39579         "351"
39580       ],
39581       [
39582         "Puerto Rico",
39583         "pr",
39584         "1",
39585         3,
39586         ["787", "939"]
39587       ],
39588       [
39589         "Qatar (‫قطر‬‎)",
39590         "qa",
39591         "974"
39592       ],
39593       [
39594         "Réunion (La Réunion)",
39595         "re",
39596         "262",
39597         0
39598       ],
39599       [
39600         "Romania (România)",
39601         "ro",
39602         "40"
39603       ],
39604       [
39605         "Russia (Россия)",
39606         "ru",
39607         "7",
39608         0
39609       ],
39610       [
39611         "Rwanda",
39612         "rw",
39613         "250"
39614       ],
39615       [
39616         "Saint Barthélemy",
39617         "bl",
39618         "590",
39619         1
39620       ],
39621       [
39622         "Saint Helena",
39623         "sh",
39624         "290"
39625       ],
39626       [
39627         "Saint Kitts and Nevis",
39628         "kn",
39629         "1869"
39630       ],
39631       [
39632         "Saint Lucia",
39633         "lc",
39634         "1758"
39635       ],
39636       [
39637         "Saint Martin (Saint-Martin (partie française))",
39638         "mf",
39639         "590",
39640         2
39641       ],
39642       [
39643         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39644         "pm",
39645         "508"
39646       ],
39647       [
39648         "Saint Vincent and the Grenadines",
39649         "vc",
39650         "1784"
39651       ],
39652       [
39653         "Samoa",
39654         "ws",
39655         "685"
39656       ],
39657       [
39658         "San Marino",
39659         "sm",
39660         "378"
39661       ],
39662       [
39663         "São Tomé and Príncipe (São Tomé e Príncipe)",
39664         "st",
39665         "239"
39666       ],
39667       [
39668         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39669         "sa",
39670         "966"
39671       ],
39672       [
39673         "Senegal (Sénégal)",
39674         "sn",
39675         "221"
39676       ],
39677       [
39678         "Serbia (Србија)",
39679         "rs",
39680         "381"
39681       ],
39682       [
39683         "Seychelles",
39684         "sc",
39685         "248"
39686       ],
39687       [
39688         "Sierra Leone",
39689         "sl",
39690         "232"
39691       ],
39692       [
39693         "Singapore",
39694         "sg",
39695         "65"
39696       ],
39697       [
39698         "Sint Maarten",
39699         "sx",
39700         "1721"
39701       ],
39702       [
39703         "Slovakia (Slovensko)",
39704         "sk",
39705         "421"
39706       ],
39707       [
39708         "Slovenia (Slovenija)",
39709         "si",
39710         "386"
39711       ],
39712       [
39713         "Solomon Islands",
39714         "sb",
39715         "677"
39716       ],
39717       [
39718         "Somalia (Soomaaliya)",
39719         "so",
39720         "252"
39721       ],
39722       [
39723         "South Africa",
39724         "za",
39725         "27"
39726       ],
39727       [
39728         "South Korea (대한민국)",
39729         "kr",
39730         "82"
39731       ],
39732       [
39733         "South Sudan (‫جنوب السودان‬‎)",
39734         "ss",
39735         "211"
39736       ],
39737       [
39738         "Spain (España)",
39739         "es",
39740         "34"
39741       ],
39742       [
39743         "Sri Lanka (ශ්‍රී ලංකාව)",
39744         "lk",
39745         "94"
39746       ],
39747       [
39748         "Sudan (‫السودان‬‎)",
39749         "sd",
39750         "249"
39751       ],
39752       [
39753         "Suriname",
39754         "sr",
39755         "597"
39756       ],
39757       [
39758         "Svalbard and Jan Mayen",
39759         "sj",
39760         "47",
39761         1
39762       ],
39763       [
39764         "Swaziland",
39765         "sz",
39766         "268"
39767       ],
39768       [
39769         "Sweden (Sverige)",
39770         "se",
39771         "46"
39772       ],
39773       [
39774         "Switzerland (Schweiz)",
39775         "ch",
39776         "41"
39777       ],
39778       [
39779         "Syria (‫سوريا‬‎)",
39780         "sy",
39781         "963"
39782       ],
39783       [
39784         "Taiwan (台灣)",
39785         "tw",
39786         "886"
39787       ],
39788       [
39789         "Tajikistan",
39790         "tj",
39791         "992"
39792       ],
39793       [
39794         "Tanzania",
39795         "tz",
39796         "255"
39797       ],
39798       [
39799         "Thailand (ไทย)",
39800         "th",
39801         "66"
39802       ],
39803       [
39804         "Timor-Leste",
39805         "tl",
39806         "670"
39807       ],
39808       [
39809         "Togo",
39810         "tg",
39811         "228"
39812       ],
39813       [
39814         "Tokelau",
39815         "tk",
39816         "690"
39817       ],
39818       [
39819         "Tonga",
39820         "to",
39821         "676"
39822       ],
39823       [
39824         "Trinidad and Tobago",
39825         "tt",
39826         "1868"
39827       ],
39828       [
39829         "Tunisia (‫تونس‬‎)",
39830         "tn",
39831         "216"
39832       ],
39833       [
39834         "Turkey (Türkiye)",
39835         "tr",
39836         "90"
39837       ],
39838       [
39839         "Turkmenistan",
39840         "tm",
39841         "993"
39842       ],
39843       [
39844         "Turks and Caicos Islands",
39845         "tc",
39846         "1649"
39847       ],
39848       [
39849         "Tuvalu",
39850         "tv",
39851         "688"
39852       ],
39853       [
39854         "U.S. Virgin Islands",
39855         "vi",
39856         "1340"
39857       ],
39858       [
39859         "Uganda",
39860         "ug",
39861         "256"
39862       ],
39863       [
39864         "Ukraine (Україна)",
39865         "ua",
39866         "380"
39867       ],
39868       [
39869         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39870         "ae",
39871         "971"
39872       ],
39873       [
39874         "United Kingdom",
39875         "gb",
39876         "44",
39877         0
39878       ],
39879       [
39880         "United States",
39881         "us",
39882         "1",
39883         0
39884       ],
39885       [
39886         "Uruguay",
39887         "uy",
39888         "598"
39889       ],
39890       [
39891         "Uzbekistan (Oʻzbekiston)",
39892         "uz",
39893         "998"
39894       ],
39895       [
39896         "Vanuatu",
39897         "vu",
39898         "678"
39899       ],
39900       [
39901         "Vatican City (Città del Vaticano)",
39902         "va",
39903         "39",
39904         1
39905       ],
39906       [
39907         "Venezuela",
39908         "ve",
39909         "58"
39910       ],
39911       [
39912         "Vietnam (Việt Nam)",
39913         "vn",
39914         "84"
39915       ],
39916       [
39917         "Wallis and Futuna (Wallis-et-Futuna)",
39918         "wf",
39919         "681"
39920       ],
39921       [
39922         "Western Sahara (‫الصحراء الغربية‬‎)",
39923         "eh",
39924         "212",
39925         1
39926       ],
39927       [
39928         "Yemen (‫اليمن‬‎)",
39929         "ye",
39930         "967"
39931       ],
39932       [
39933         "Zambia",
39934         "zm",
39935         "260"
39936       ],
39937       [
39938         "Zimbabwe",
39939         "zw",
39940         "263"
39941       ],
39942       [
39943         "Åland Islands",
39944         "ax",
39945         "358",
39946         1
39947       ]
39948   ];
39949   
39950   return d;
39951 }/**
39952 *    This script refer to:
39953 *    Title: International Telephone Input
39954 *    Author: Jack O'Connor
39955 *    Code version:  v12.1.12
39956 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39957 **/
39958
39959 /**
39960  * @class Roo.bootstrap.PhoneInput
39961  * @extends Roo.bootstrap.TriggerField
39962  * An input with International dial-code selection
39963  
39964  * @cfg {String} defaultDialCode default '+852'
39965  * @cfg {Array} preferedCountries default []
39966   
39967  * @constructor
39968  * Create a new PhoneInput.
39969  * @param {Object} config Configuration options
39970  */
39971
39972 Roo.bootstrap.PhoneInput = function(config) {
39973     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39974 };
39975
39976 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39977         
39978         listWidth: undefined,
39979         
39980         selectedClass: 'active',
39981         
39982         invalidClass : "has-warning",
39983         
39984         validClass: 'has-success',
39985         
39986         allowed: '0123456789',
39987         
39988         max_length: 15,
39989         
39990         /**
39991          * @cfg {String} defaultDialCode The default dial code when initializing the input
39992          */
39993         defaultDialCode: '+852',
39994         
39995         /**
39996          * @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
39997          */
39998         preferedCountries: false,
39999         
40000         getAutoCreate : function()
40001         {
40002             var data = Roo.bootstrap.PhoneInputData();
40003             var align = this.labelAlign || this.parentLabelAlign();
40004             var id = Roo.id();
40005             
40006             this.allCountries = [];
40007             this.dialCodeMapping = [];
40008             
40009             for (var i = 0; i < data.length; i++) {
40010               var c = data[i];
40011               this.allCountries[i] = {
40012                 name: c[0],
40013                 iso2: c[1],
40014                 dialCode: c[2],
40015                 priority: c[3] || 0,
40016                 areaCodes: c[4] || null
40017               };
40018               this.dialCodeMapping[c[2]] = {
40019                   name: c[0],
40020                   iso2: c[1],
40021                   priority: c[3] || 0,
40022                   areaCodes: c[4] || null
40023               };
40024             }
40025             
40026             var cfg = {
40027                 cls: 'form-group',
40028                 cn: []
40029             };
40030             
40031             var input =  {
40032                 tag: 'input',
40033                 id : id,
40034                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40035                 maxlength: this.max_length,
40036                 cls : 'form-control tel-input',
40037                 autocomplete: 'new-password'
40038             };
40039             
40040             var hiddenInput = {
40041                 tag: 'input',
40042                 type: 'hidden',
40043                 cls: 'hidden-tel-input'
40044             };
40045             
40046             if (this.name) {
40047                 hiddenInput.name = this.name;
40048             }
40049             
40050             if (this.disabled) {
40051                 input.disabled = true;
40052             }
40053             
40054             var flag_container = {
40055                 tag: 'div',
40056                 cls: 'flag-box',
40057                 cn: [
40058                     {
40059                         tag: 'div',
40060                         cls: 'flag'
40061                     },
40062                     {
40063                         tag: 'div',
40064                         cls: 'caret'
40065                     }
40066                 ]
40067             };
40068             
40069             var box = {
40070                 tag: 'div',
40071                 cls: this.hasFeedback ? 'has-feedback' : '',
40072                 cn: [
40073                     hiddenInput,
40074                     input,
40075                     {
40076                         tag: 'input',
40077                         cls: 'dial-code-holder',
40078                         disabled: true
40079                     }
40080                 ]
40081             };
40082             
40083             var container = {
40084                 cls: 'roo-select2-container input-group',
40085                 cn: [
40086                     flag_container,
40087                     box
40088                 ]
40089             };
40090             
40091             if (this.fieldLabel.length) {
40092                 var indicator = {
40093                     tag: 'i',
40094                     tooltip: 'This field is required'
40095                 };
40096                 
40097                 var label = {
40098                     tag: 'label',
40099                     'for':  id,
40100                     cls: 'control-label',
40101                     cn: []
40102                 };
40103                 
40104                 var label_text = {
40105                     tag: 'span',
40106                     html: this.fieldLabel
40107                 };
40108                 
40109                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40110                 label.cn = [
40111                     indicator,
40112                     label_text
40113                 ];
40114                 
40115                 if(this.indicatorpos == 'right') {
40116                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40117                     label.cn = [
40118                         label_text,
40119                         indicator
40120                     ];
40121                 }
40122                 
40123                 if(align == 'left') {
40124                     container = {
40125                         tag: 'div',
40126                         cn: [
40127                             container
40128                         ]
40129                     };
40130                     
40131                     if(this.labelWidth > 12){
40132                         label.style = "width: " + this.labelWidth + 'px';
40133                     }
40134                     if(this.labelWidth < 13 && this.labelmd == 0){
40135                         this.labelmd = this.labelWidth;
40136                     }
40137                     if(this.labellg > 0){
40138                         label.cls += ' col-lg-' + this.labellg;
40139                         input.cls += ' col-lg-' + (12 - this.labellg);
40140                     }
40141                     if(this.labelmd > 0){
40142                         label.cls += ' col-md-' + this.labelmd;
40143                         container.cls += ' col-md-' + (12 - this.labelmd);
40144                     }
40145                     if(this.labelsm > 0){
40146                         label.cls += ' col-sm-' + this.labelsm;
40147                         container.cls += ' col-sm-' + (12 - this.labelsm);
40148                     }
40149                     if(this.labelxs > 0){
40150                         label.cls += ' col-xs-' + this.labelxs;
40151                         container.cls += ' col-xs-' + (12 - this.labelxs);
40152                     }
40153                 }
40154             }
40155             
40156             cfg.cn = [
40157                 label,
40158                 container
40159             ];
40160             
40161             var settings = this;
40162             
40163             ['xs','sm','md','lg'].map(function(size){
40164                 if (settings[size]) {
40165                     cfg.cls += ' col-' + size + '-' + settings[size];
40166                 }
40167             });
40168             
40169             this.store = new Roo.data.Store({
40170                 proxy : new Roo.data.MemoryProxy({}),
40171                 reader : new Roo.data.JsonReader({
40172                     fields : [
40173                         {
40174                             'name' : 'name',
40175                             'type' : 'string'
40176                         },
40177                         {
40178                             'name' : 'iso2',
40179                             'type' : 'string'
40180                         },
40181                         {
40182                             'name' : 'dialCode',
40183                             'type' : 'string'
40184                         },
40185                         {
40186                             'name' : 'priority',
40187                             'type' : 'string'
40188                         },
40189                         {
40190                             'name' : 'areaCodes',
40191                             'type' : 'string'
40192                         }
40193                     ]
40194                 })
40195             });
40196             
40197             if(!this.preferedCountries) {
40198                 this.preferedCountries = [
40199                     'hk',
40200                     'gb',
40201                     'us'
40202                 ];
40203             }
40204             
40205             var p = this.preferedCountries.reverse();
40206             
40207             if(p) {
40208                 for (var i = 0; i < p.length; i++) {
40209                     for (var j = 0; j < this.allCountries.length; j++) {
40210                         if(this.allCountries[j].iso2 == p[i]) {
40211                             var t = this.allCountries[j];
40212                             this.allCountries.splice(j,1);
40213                             this.allCountries.unshift(t);
40214                         }
40215                     } 
40216                 }
40217             }
40218             
40219             this.store.proxy.data = {
40220                 success: true,
40221                 data: this.allCountries
40222             };
40223             
40224             return cfg;
40225         },
40226         
40227         initEvents : function()
40228         {
40229             this.createList();
40230             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40231             
40232             this.indicator = this.indicatorEl();
40233             this.flag = this.flagEl();
40234             this.dialCodeHolder = this.dialCodeHolderEl();
40235             
40236             this.trigger = this.el.select('div.flag-box',true).first();
40237             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40238             
40239             var _this = this;
40240             
40241             (function(){
40242                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40243                 _this.list.setWidth(lw);
40244             }).defer(100);
40245             
40246             this.list.on('mouseover', this.onViewOver, this);
40247             this.list.on('mousemove', this.onViewMove, this);
40248             this.inputEl().on("keyup", this.onKeyUp, this);
40249             this.inputEl().on("keypress", this.onKeyPress, this);
40250             
40251             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40252
40253             this.view = new Roo.View(this.list, this.tpl, {
40254                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40255             });
40256             
40257             this.view.on('click', this.onViewClick, this);
40258             this.setValue(this.defaultDialCode);
40259         },
40260         
40261         onTriggerClick : function(e)
40262         {
40263             Roo.log('trigger click');
40264             if(this.disabled){
40265                 return;
40266             }
40267             
40268             if(this.isExpanded()){
40269                 this.collapse();
40270                 this.hasFocus = false;
40271             }else {
40272                 this.store.load({});
40273                 this.hasFocus = true;
40274                 this.expand();
40275             }
40276         },
40277         
40278         isExpanded : function()
40279         {
40280             return this.list.isVisible();
40281         },
40282         
40283         collapse : function()
40284         {
40285             if(!this.isExpanded()){
40286                 return;
40287             }
40288             this.list.hide();
40289             Roo.get(document).un('mousedown', this.collapseIf, this);
40290             Roo.get(document).un('mousewheel', this.collapseIf, this);
40291             this.fireEvent('collapse', this);
40292             this.validate();
40293         },
40294         
40295         expand : function()
40296         {
40297             Roo.log('expand');
40298
40299             if(this.isExpanded() || !this.hasFocus){
40300                 return;
40301             }
40302             
40303             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40304             this.list.setWidth(lw);
40305             
40306             this.list.show();
40307             this.restrictHeight();
40308             
40309             Roo.get(document).on('mousedown', this.collapseIf, this);
40310             Roo.get(document).on('mousewheel', this.collapseIf, this);
40311             
40312             this.fireEvent('expand', this);
40313         },
40314         
40315         restrictHeight : function()
40316         {
40317             this.list.alignTo(this.inputEl(), this.listAlign);
40318             this.list.alignTo(this.inputEl(), this.listAlign);
40319         },
40320         
40321         onViewOver : function(e, t)
40322         {
40323             if(this.inKeyMode){
40324                 return;
40325             }
40326             var item = this.view.findItemFromChild(t);
40327             
40328             if(item){
40329                 var index = this.view.indexOf(item);
40330                 this.select(index, false);
40331             }
40332         },
40333
40334         // private
40335         onViewClick : function(view, doFocus, el, e)
40336         {
40337             var index = this.view.getSelectedIndexes()[0];
40338             
40339             var r = this.store.getAt(index);
40340             
40341             if(r){
40342                 this.onSelect(r, index);
40343             }
40344             if(doFocus !== false && !this.blockFocus){
40345                 this.inputEl().focus();
40346             }
40347         },
40348         
40349         onViewMove : function(e, t)
40350         {
40351             this.inKeyMode = false;
40352         },
40353         
40354         select : function(index, scrollIntoView)
40355         {
40356             this.selectedIndex = index;
40357             this.view.select(index);
40358             if(scrollIntoView !== false){
40359                 var el = this.view.getNode(index);
40360                 if(el){
40361                     this.list.scrollChildIntoView(el, false);
40362                 }
40363             }
40364         },
40365         
40366         createList : function()
40367         {
40368             this.list = Roo.get(document.body).createChild({
40369                 tag: 'ul',
40370                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40371                 style: 'display:none'
40372             });
40373             
40374             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40375         },
40376         
40377         collapseIf : function(e)
40378         {
40379             var in_combo  = e.within(this.el);
40380             var in_list =  e.within(this.list);
40381             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40382             
40383             if (in_combo || in_list || is_list) {
40384                 return;
40385             }
40386             this.collapse();
40387         },
40388         
40389         onSelect : function(record, index)
40390         {
40391             if(this.fireEvent('beforeselect', this, record, index) !== false){
40392                 
40393                 this.setFlagClass(record.data.iso2);
40394                 this.setDialCode(record.data.dialCode);
40395                 this.hasFocus = false;
40396                 this.collapse();
40397                 this.fireEvent('select', this, record, index);
40398             }
40399         },
40400         
40401         flagEl : function()
40402         {
40403             var flag = this.el.select('div.flag',true).first();
40404             if(!flag){
40405                 return false;
40406             }
40407             return flag;
40408         },
40409         
40410         dialCodeHolderEl : function()
40411         {
40412             var d = this.el.select('input.dial-code-holder',true).first();
40413             if(!d){
40414                 return false;
40415             }
40416             return d;
40417         },
40418         
40419         setDialCode : function(v)
40420         {
40421             this.dialCodeHolder.dom.value = '+'+v;
40422         },
40423         
40424         setFlagClass : function(n)
40425         {
40426             this.flag.dom.className = 'flag '+n;
40427         },
40428         
40429         getValue : function()
40430         {
40431             var v = this.inputEl().getValue();
40432             if(this.dialCodeHolder) {
40433                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40434             }
40435             return v;
40436         },
40437         
40438         setValue : function(v)
40439         {
40440             var d = this.getDialCode(v);
40441             
40442             //invalid dial code
40443             if(v.length == 0 || !d || d.length == 0) {
40444                 if(this.rendered){
40445                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40446                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40447                 }
40448                 return;
40449             }
40450             
40451             //valid dial code
40452             this.setFlagClass(this.dialCodeMapping[d].iso2);
40453             this.setDialCode(d);
40454             this.inputEl().dom.value = v.replace('+'+d,'');
40455             this.hiddenEl().dom.value = this.getValue();
40456             
40457             this.validate();
40458         },
40459         
40460         getDialCode : function(v)
40461         {
40462             v = v ||  '';
40463             
40464             if (v.length == 0) {
40465                 return this.dialCodeHolder.dom.value;
40466             }
40467             
40468             var dialCode = "";
40469             if (v.charAt(0) != "+") {
40470                 return false;
40471             }
40472             var numericChars = "";
40473             for (var i = 1; i < v.length; i++) {
40474               var c = v.charAt(i);
40475               if (!isNaN(c)) {
40476                 numericChars += c;
40477                 if (this.dialCodeMapping[numericChars]) {
40478                   dialCode = v.substr(1, i);
40479                 }
40480                 if (numericChars.length == 4) {
40481                   break;
40482                 }
40483               }
40484             }
40485             return dialCode;
40486         },
40487         
40488         reset : function()
40489         {
40490             this.setValue(this.defaultDialCode);
40491             this.validate();
40492         },
40493         
40494         hiddenEl : function()
40495         {
40496             return this.el.select('input.hidden-tel-input',true).first();
40497         },
40498         
40499         // after setting val
40500         onKeyUp : function(e){
40501             this.setValue(this.getValue());
40502         },
40503         
40504         onKeyPress : function(e){
40505             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40506                 e.stopEvent();
40507             }
40508         }
40509         
40510 });
40511 /**
40512  * @class Roo.bootstrap.MoneyField
40513  * @extends Roo.bootstrap.ComboBox
40514  * Bootstrap MoneyField class
40515  * 
40516  * @constructor
40517  * Create a new MoneyField.
40518  * @param {Object} config Configuration options
40519  */
40520
40521 Roo.bootstrap.MoneyField = function(config) {
40522     
40523     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40524     
40525 };
40526
40527 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40528     
40529     /**
40530      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40531      */
40532     allowDecimals : true,
40533     /**
40534      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40535      */
40536     decimalSeparator : ".",
40537     /**
40538      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40539      */
40540     decimalPrecision : 0,
40541     /**
40542      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40543      */
40544     allowNegative : true,
40545     /**
40546      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40547      */
40548     allowZero: true,
40549     /**
40550      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40551      */
40552     minValue : Number.NEGATIVE_INFINITY,
40553     /**
40554      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40555      */
40556     maxValue : Number.MAX_VALUE,
40557     /**
40558      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40559      */
40560     minText : "The minimum value for this field is {0}",
40561     /**
40562      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40563      */
40564     maxText : "The maximum value for this field is {0}",
40565     /**
40566      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40567      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40568      */
40569     nanText : "{0} is not a valid number",
40570     /**
40571      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40572      */
40573     castInt : true,
40574     /**
40575      * @cfg {String} defaults currency of the MoneyField
40576      * value should be in lkey
40577      */
40578     defaultCurrency : false,
40579     /**
40580      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40581      */
40582     thousandsDelimiter : false,
40583     /**
40584      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40585      */
40586     max_length: false,
40587     
40588     inputlg : 9,
40589     inputmd : 9,
40590     inputsm : 9,
40591     inputxs : 6,
40592     
40593     store : false,
40594     
40595     getAutoCreate : function()
40596     {
40597         var align = this.labelAlign || this.parentLabelAlign();
40598         
40599         var id = Roo.id();
40600
40601         var cfg = {
40602             cls: 'form-group',
40603             cn: []
40604         };
40605
40606         var input =  {
40607             tag: 'input',
40608             id : id,
40609             cls : 'form-control roo-money-amount-input',
40610             autocomplete: 'new-password'
40611         };
40612         
40613         var hiddenInput = {
40614             tag: 'input',
40615             type: 'hidden',
40616             id: Roo.id(),
40617             cls: 'hidden-number-input'
40618         };
40619         
40620         if(this.max_length) {
40621             input.maxlength = this.max_length; 
40622         }
40623         
40624         if (this.name) {
40625             hiddenInput.name = this.name;
40626         }
40627
40628         if (this.disabled) {
40629             input.disabled = true;
40630         }
40631
40632         var clg = 12 - this.inputlg;
40633         var cmd = 12 - this.inputmd;
40634         var csm = 12 - this.inputsm;
40635         var cxs = 12 - this.inputxs;
40636         
40637         var container = {
40638             tag : 'div',
40639             cls : 'row roo-money-field',
40640             cn : [
40641                 {
40642                     tag : 'div',
40643                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40644                     cn : [
40645                         {
40646                             tag : 'div',
40647                             cls: 'roo-select2-container input-group',
40648                             cn: [
40649                                 {
40650                                     tag : 'input',
40651                                     cls : 'form-control roo-money-currency-input',
40652                                     autocomplete: 'new-password',
40653                                     readOnly : 1,
40654                                     name : this.currencyName
40655                                 },
40656                                 {
40657                                     tag :'span',
40658                                     cls : 'input-group-addon',
40659                                     cn : [
40660                                         {
40661                                             tag: 'span',
40662                                             cls: 'caret'
40663                                         }
40664                                     ]
40665                                 }
40666                             ]
40667                         }
40668                     ]
40669                 },
40670                 {
40671                     tag : 'div',
40672                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40673                     cn : [
40674                         {
40675                             tag: 'div',
40676                             cls: this.hasFeedback ? 'has-feedback' : '',
40677                             cn: [
40678                                 input
40679                             ]
40680                         }
40681                     ]
40682                 }
40683             ]
40684             
40685         };
40686         
40687         if (this.fieldLabel.length) {
40688             var indicator = {
40689                 tag: 'i',
40690                 tooltip: 'This field is required'
40691             };
40692
40693             var label = {
40694                 tag: 'label',
40695                 'for':  id,
40696                 cls: 'control-label',
40697                 cn: []
40698             };
40699
40700             var label_text = {
40701                 tag: 'span',
40702                 html: this.fieldLabel
40703             };
40704
40705             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40706             label.cn = [
40707                 indicator,
40708                 label_text
40709             ];
40710
40711             if(this.indicatorpos == 'right') {
40712                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40713                 label.cn = [
40714                     label_text,
40715                     indicator
40716                 ];
40717             }
40718
40719             if(align == 'left') {
40720                 container = {
40721                     tag: 'div',
40722                     cn: [
40723                         container
40724                     ]
40725                 };
40726
40727                 if(this.labelWidth > 12){
40728                     label.style = "width: " + this.labelWidth + 'px';
40729                 }
40730                 if(this.labelWidth < 13 && this.labelmd == 0){
40731                     this.labelmd = this.labelWidth;
40732                 }
40733                 if(this.labellg > 0){
40734                     label.cls += ' col-lg-' + this.labellg;
40735                     input.cls += ' col-lg-' + (12 - this.labellg);
40736                 }
40737                 if(this.labelmd > 0){
40738                     label.cls += ' col-md-' + this.labelmd;
40739                     container.cls += ' col-md-' + (12 - this.labelmd);
40740                 }
40741                 if(this.labelsm > 0){
40742                     label.cls += ' col-sm-' + this.labelsm;
40743                     container.cls += ' col-sm-' + (12 - this.labelsm);
40744                 }
40745                 if(this.labelxs > 0){
40746                     label.cls += ' col-xs-' + this.labelxs;
40747                     container.cls += ' col-xs-' + (12 - this.labelxs);
40748                 }
40749             }
40750         }
40751
40752         cfg.cn = [
40753             label,
40754             container,
40755             hiddenInput
40756         ];
40757         
40758         var settings = this;
40759
40760         ['xs','sm','md','lg'].map(function(size){
40761             if (settings[size]) {
40762                 cfg.cls += ' col-' + size + '-' + settings[size];
40763             }
40764         });
40765         
40766         return cfg;
40767     },
40768     
40769     initEvents : function()
40770     {
40771         this.indicator = this.indicatorEl();
40772         
40773         this.initCurrencyEvent();
40774         
40775         this.initNumberEvent();
40776     },
40777     
40778     initCurrencyEvent : function()
40779     {
40780         if (!this.store) {
40781             throw "can not find store for combo";
40782         }
40783         
40784         this.store = Roo.factory(this.store, Roo.data);
40785         this.store.parent = this;
40786         
40787         this.createList();
40788         
40789         this.triggerEl = this.el.select('.input-group-addon', true).first();
40790         
40791         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40792         
40793         var _this = this;
40794         
40795         (function(){
40796             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40797             _this.list.setWidth(lw);
40798         }).defer(100);
40799         
40800         this.list.on('mouseover', this.onViewOver, this);
40801         this.list.on('mousemove', this.onViewMove, this);
40802         this.list.on('scroll', this.onViewScroll, this);
40803         
40804         if(!this.tpl){
40805             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40806         }
40807         
40808         this.view = new Roo.View(this.list, this.tpl, {
40809             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40810         });
40811         
40812         this.view.on('click', this.onViewClick, this);
40813         
40814         this.store.on('beforeload', this.onBeforeLoad, this);
40815         this.store.on('load', this.onLoad, this);
40816         this.store.on('loadexception', this.onLoadException, this);
40817         
40818         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40819             "up" : function(e){
40820                 this.inKeyMode = true;
40821                 this.selectPrev();
40822             },
40823
40824             "down" : function(e){
40825                 if(!this.isExpanded()){
40826                     this.onTriggerClick();
40827                 }else{
40828                     this.inKeyMode = true;
40829                     this.selectNext();
40830                 }
40831             },
40832
40833             "enter" : function(e){
40834                 this.collapse();
40835                 
40836                 if(this.fireEvent("specialkey", this, e)){
40837                     this.onViewClick(false);
40838                 }
40839                 
40840                 return true;
40841             },
40842
40843             "esc" : function(e){
40844                 this.collapse();
40845             },
40846
40847             "tab" : function(e){
40848                 this.collapse();
40849                 
40850                 if(this.fireEvent("specialkey", this, e)){
40851                     this.onViewClick(false);
40852                 }
40853                 
40854                 return true;
40855             },
40856
40857             scope : this,
40858
40859             doRelay : function(foo, bar, hname){
40860                 if(hname == 'down' || this.scope.isExpanded()){
40861                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40862                 }
40863                 return true;
40864             },
40865
40866             forceKeyDown: true
40867         });
40868         
40869         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40870         
40871     },
40872     
40873     initNumberEvent : function(e)
40874     {
40875         this.inputEl().on("keydown" , this.fireKey,  this);
40876         this.inputEl().on("focus", this.onFocus,  this);
40877         this.inputEl().on("blur", this.onBlur,  this);
40878         
40879         this.inputEl().relayEvent('keyup', this);
40880         
40881         if(this.indicator){
40882             this.indicator.addClass('invisible');
40883         }
40884  
40885         this.originalValue = this.getValue();
40886         
40887         if(this.validationEvent == 'keyup'){
40888             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40889             this.inputEl().on('keyup', this.filterValidation, this);
40890         }
40891         else if(this.validationEvent !== false){
40892             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40893         }
40894         
40895         if(this.selectOnFocus){
40896             this.on("focus", this.preFocus, this);
40897             
40898         }
40899         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40900             this.inputEl().on("keypress", this.filterKeys, this);
40901         } else {
40902             this.inputEl().relayEvent('keypress', this);
40903         }
40904         
40905         var allowed = "0123456789";
40906         
40907         if(this.allowDecimals){
40908             allowed += this.decimalSeparator;
40909         }
40910         
40911         if(this.allowNegative){
40912             allowed += "-";
40913         }
40914         
40915         if(this.thousandsDelimiter) {
40916             allowed += ",";
40917         }
40918         
40919         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40920         
40921         var keyPress = function(e){
40922             
40923             var k = e.getKey();
40924             
40925             var c = e.getCharCode();
40926             
40927             if(
40928                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40929                     allowed.indexOf(String.fromCharCode(c)) === -1
40930             ){
40931                 e.stopEvent();
40932                 return;
40933             }
40934             
40935             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40936                 return;
40937             }
40938             
40939             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40940                 e.stopEvent();
40941             }
40942         };
40943         
40944         this.inputEl().on("keypress", keyPress, this);
40945         
40946     },
40947     
40948     onTriggerClick : function(e)
40949     {   
40950         if(this.disabled){
40951             return;
40952         }
40953         
40954         this.page = 0;
40955         this.loadNext = false;
40956         
40957         if(this.isExpanded()){
40958             this.collapse();
40959             return;
40960         }
40961         
40962         this.hasFocus = true;
40963         
40964         if(this.triggerAction == 'all') {
40965             this.doQuery(this.allQuery, true);
40966             return;
40967         }
40968         
40969         this.doQuery(this.getRawValue());
40970     },
40971     
40972     getCurrency : function()
40973     {   
40974         var v = this.currencyEl().getValue();
40975         
40976         return v;
40977     },
40978     
40979     restrictHeight : function()
40980     {
40981         this.list.alignTo(this.currencyEl(), this.listAlign);
40982         this.list.alignTo(this.currencyEl(), this.listAlign);
40983     },
40984     
40985     onViewClick : function(view, doFocus, el, e)
40986     {
40987         var index = this.view.getSelectedIndexes()[0];
40988         
40989         var r = this.store.getAt(index);
40990         
40991         if(r){
40992             this.onSelect(r, index);
40993         }
40994     },
40995     
40996     onSelect : function(record, index){
40997         
40998         if(this.fireEvent('beforeselect', this, record, index) !== false){
40999         
41000             this.setFromCurrencyData(index > -1 ? record.data : false);
41001             
41002             this.collapse();
41003             
41004             this.fireEvent('select', this, record, index);
41005         }
41006     },
41007     
41008     setFromCurrencyData : function(o)
41009     {
41010         var currency = '';
41011         
41012         this.lastCurrency = o;
41013         
41014         if (this.currencyField) {
41015             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41016         } else {
41017             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41018         }
41019         
41020         this.lastSelectionText = currency;
41021         
41022         //setting default currency
41023         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41024             this.setCurrency(this.defaultCurrency);
41025             return;
41026         }
41027         
41028         this.setCurrency(currency);
41029     },
41030     
41031     setFromData : function(o)
41032     {
41033         var c = {};
41034         
41035         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41036         
41037         this.setFromCurrencyData(c);
41038         
41039         var value = '';
41040         
41041         if (this.name) {
41042             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41043         } else {
41044             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41045         }
41046         
41047         this.setValue(value);
41048         
41049     },
41050     
41051     setCurrency : function(v)
41052     {   
41053         this.currencyValue = v;
41054         
41055         if(this.rendered){
41056             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41057             this.validate();
41058         }
41059     },
41060     
41061     setValue : function(v)
41062     {
41063         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41064         
41065         this.value = v;
41066         
41067         if(this.rendered){
41068             
41069             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41070             
41071             this.inputEl().dom.value = (v == '') ? '' :
41072                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41073             
41074             if(!this.allowZero && v === '0') {
41075                 this.hiddenEl().dom.value = '';
41076                 this.inputEl().dom.value = '';
41077             }
41078             
41079             this.validate();
41080         }
41081     },
41082     
41083     getRawValue : function()
41084     {
41085         var v = this.inputEl().getValue();
41086         
41087         return v;
41088     },
41089     
41090     getValue : function()
41091     {
41092         return this.fixPrecision(this.parseValue(this.getRawValue()));
41093     },
41094     
41095     parseValue : function(value)
41096     {
41097         if(this.thousandsDelimiter) {
41098             value += "";
41099             r = new RegExp(",", "g");
41100             value = value.replace(r, "");
41101         }
41102         
41103         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41104         return isNaN(value) ? '' : value;
41105         
41106     },
41107     
41108     fixPrecision : function(value)
41109     {
41110         if(this.thousandsDelimiter) {
41111             value += "";
41112             r = new RegExp(",", "g");
41113             value = value.replace(r, "");
41114         }
41115         
41116         var nan = isNaN(value);
41117         
41118         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41119             return nan ? '' : value;
41120         }
41121         return parseFloat(value).toFixed(this.decimalPrecision);
41122     },
41123     
41124     decimalPrecisionFcn : function(v)
41125     {
41126         return Math.floor(v);
41127     },
41128     
41129     validateValue : function(value)
41130     {
41131         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41132             return false;
41133         }
41134         
41135         var num = this.parseValue(value);
41136         
41137         if(isNaN(num)){
41138             this.markInvalid(String.format(this.nanText, value));
41139             return false;
41140         }
41141         
41142         if(num < this.minValue){
41143             this.markInvalid(String.format(this.minText, this.minValue));
41144             return false;
41145         }
41146         
41147         if(num > this.maxValue){
41148             this.markInvalid(String.format(this.maxText, this.maxValue));
41149             return false;
41150         }
41151         
41152         return true;
41153     },
41154     
41155     validate : function()
41156     {
41157         if(this.disabled || this.allowBlank){
41158             this.markValid();
41159             return true;
41160         }
41161         
41162         var currency = this.getCurrency();
41163         
41164         if(this.validateValue(this.getRawValue()) && currency.length){
41165             this.markValid();
41166             return true;
41167         }
41168         
41169         this.markInvalid();
41170         return false;
41171     },
41172     
41173     getName: function()
41174     {
41175         return this.name;
41176     },
41177     
41178     beforeBlur : function()
41179     {
41180         if(!this.castInt){
41181             return;
41182         }
41183         
41184         var v = this.parseValue(this.getRawValue());
41185         
41186         if(v || v == 0){
41187             this.setValue(v);
41188         }
41189     },
41190     
41191     onBlur : function()
41192     {
41193         this.beforeBlur();
41194         
41195         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41196             //this.el.removeClass(this.focusClass);
41197         }
41198         
41199         this.hasFocus = false;
41200         
41201         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41202             this.validate();
41203         }
41204         
41205         var v = this.getValue();
41206         
41207         if(String(v) !== String(this.startValue)){
41208             this.fireEvent('change', this, v, this.startValue);
41209         }
41210         
41211         this.fireEvent("blur", this);
41212     },
41213     
41214     inputEl : function()
41215     {
41216         return this.el.select('.roo-money-amount-input', true).first();
41217     },
41218     
41219     currencyEl : function()
41220     {
41221         return this.el.select('.roo-money-currency-input', true).first();
41222     },
41223     
41224     hiddenEl : function()
41225     {
41226         return this.el.select('input.hidden-number-input',true).first();
41227     }
41228     
41229 });