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('show');
3870                     
3871                         ce.setHeight(ce.getHeight()); // resize it ...
3872                         ce.removeClass('collapsing');
3873                         ce.addClass('collapsing');
3874                     
3875                     // now flag it as moving..
3876                     
3877                     
3878                     (function() { ce.removeClass('collapsing'); }).defer(500);
3879                 } else {
3880                     ce.addClass('collapsing');
3881                     ce.removeClass('show');
3882                     (function() {
3883                         ce.removeClass('collapsing');
3884                         ce.addClass('collapse');
3885                     }).defer(200);
3886                     
3887                 }
3888             }
3889             
3890         }, this);
3891         
3892         var mark = {
3893             tag: "div",
3894             cls:"x-dlg-mask"
3895         };
3896         
3897         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3898         
3899         var size = this.el.getSize();
3900         this.maskEl.setSize(size.width, size.height);
3901         this.maskEl.enableDisplayMode("block");
3902         this.maskEl.hide();
3903         
3904         if(this.loadMask){
3905             this.maskEl.show();
3906         }
3907     },
3908     
3909     
3910     getChildContainer : function()
3911     {
3912         if (this.el.select('.collapse').getCount()) {
3913             return this.el.select('.collapse',true).first();
3914         }
3915         
3916         return this.el;
3917     },
3918     
3919     mask : function()
3920     {
3921         this.maskEl.show();
3922     },
3923     
3924     unmask : function()
3925     {
3926         this.maskEl.hide();
3927     } 
3928     
3929     
3930     
3931     
3932 });
3933
3934
3935
3936  
3937
3938  /*
3939  * - LGPL
3940  *
3941  * navbar
3942  * 
3943  */
3944
3945 /**
3946  * @class Roo.bootstrap.NavSimplebar
3947  * @extends Roo.bootstrap.Navbar
3948  * Bootstrap Sidebar class
3949  *
3950  * @cfg {Boolean} inverse is inverted color
3951  * 
3952  * @cfg {String} type (nav | pills | tabs)
3953  * @cfg {Boolean} arrangement stacked | justified
3954  * @cfg {String} align (left | right) alignment
3955  * 
3956  * @cfg {Boolean} main (true|false) main nav bar? default false
3957  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3958  * 
3959  * @cfg {String} tag (header|footer|nav|div) default is nav 
3960
3961  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3962  * 
3963  * 
3964  * @constructor
3965  * Create a new Sidebar
3966  * @param {Object} config The config object
3967  */
3968
3969
3970 Roo.bootstrap.NavSimplebar = function(config){
3971     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3972 };
3973
3974 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3975     
3976     inverse: false,
3977     
3978     type: false,
3979     arrangement: '',
3980     align : false,
3981     
3982     weight : 'light',
3983     
3984     main : false,
3985     
3986     
3987     tag : false,
3988     
3989     
3990     getAutoCreate : function(){
3991         
3992         
3993         var cfg = {
3994             tag : this.tag || 'div',
3995             cls : 'navbar navbar-expand-lg'
3996         };
3997         if (['light','white'].indexOf(this.weight) > -1) {
3998             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3999         }
4000         cfg.cls += ' bg-' + this.weight;
4001         
4002           
4003         
4004         cfg.cn = [
4005             {
4006                 cls: 'nav',
4007                 tag : 'ul'
4008             }
4009         ];
4010         
4011          
4012         this.type = this.type || 'nav';
4013         if (['tabs','pills'].indexOf(this.type)!==-1) {
4014             cfg.cn[0].cls += ' nav-' + this.type
4015         
4016         
4017         } else {
4018             if (this.type!=='nav') {
4019                 Roo.log('nav type must be nav/tabs/pills')
4020             }
4021             cfg.cn[0].cls += ' navbar-nav'
4022         }
4023         
4024         
4025         
4026         
4027         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4028             cfg.cn[0].cls += ' nav-' + this.arrangement;
4029         }
4030         
4031         
4032         if (this.align === 'right') {
4033             cfg.cn[0].cls += ' navbar-right';
4034         }
4035         
4036         if (this.inverse) {
4037             cfg.cls += ' navbar-inverse';
4038             
4039         }
4040         
4041         
4042         return cfg;
4043     
4044         
4045     }
4046     
4047     
4048     
4049 });
4050
4051
4052
4053  
4054
4055  
4056        /*
4057  * - LGPL
4058  *
4059  * navbar
4060  * navbar-fixed-top
4061  * navbar-expand-md  fixed-top 
4062  */
4063
4064 /**
4065  * @class Roo.bootstrap.NavHeaderbar
4066  * @extends Roo.bootstrap.NavSimplebar
4067  * Bootstrap Sidebar class
4068  *
4069  * @cfg {String} brand what is brand
4070  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4071  * @cfg {String} brand_href href of the brand
4072  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4073  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4074  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4075  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4076  * 
4077  * @constructor
4078  * Create a new Sidebar
4079  * @param {Object} config The config object
4080  */
4081
4082
4083 Roo.bootstrap.NavHeaderbar = function(config){
4084     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4085       
4086 };
4087
4088 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4089     
4090     position: '',
4091     brand: '',
4092     brand_href: false,
4093     srButton : true,
4094     autohide : false,
4095     desktopCenter : false,
4096    
4097     
4098     getAutoCreate : function(){
4099         
4100         var   cfg = {
4101             tag: this.nav || 'nav',
4102             cls: 'navbar navbar-expand-md',
4103             role: 'navigation',
4104             cn: []
4105         };
4106         
4107         var cn = cfg.cn;
4108         if (this.desktopCenter) {
4109             cn.push({cls : 'container', cn : []});
4110             cn = cn[0].cn;
4111         }
4112         
4113         if(this.srButton){
4114             var btn = {
4115                 tag: 'button',
4116                 type: 'button',
4117                 cls: 'navbar-toggle navbar-toggler',
4118                 'data-toggle': 'collapse',
4119                 cn: [
4120                     {
4121                         tag: 'span',
4122                         cls: 'sr-only',
4123                         html: 'Toggle navigation'
4124                     },
4125                     {
4126                         tag: 'span',
4127                         cls: 'icon-bar navbar-toggler-icon'
4128                     },
4129                     {
4130                         tag: 'span',
4131                         cls: 'icon-bar'
4132                     },
4133                     {
4134                         tag: 'span',
4135                         cls: 'icon-bar'
4136                     }
4137                 ]
4138             };
4139             
4140             cn.push( Roo.bootstrap.version == 4 ? btn : {
4141                 tag: 'div',
4142                 cls: 'navbar-header',
4143                 cn: [
4144                     btn
4145                 ]
4146             });
4147         }
4148         
4149         cn.push({
4150             tag: 'div',
4151             cls: 'collapse navbar-collapse',
4152             cn : []
4153         });
4154         
4155         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4156         
4157         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4158             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4159             
4160             // tag can override this..
4161             
4162             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4163         }
4164         
4165         if (this.brand !== '') {
4166             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4167             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4168                 tag: 'a',
4169                 href: this.brand_href ? this.brand_href : '#',
4170                 cls: 'navbar-brand',
4171                 cn: [
4172                 this.brand
4173                 ]
4174             });
4175         }
4176         
4177         if(this.main){
4178             cfg.cls += ' main-nav';
4179         }
4180         
4181         
4182         return cfg;
4183
4184         
4185     },
4186     getHeaderChildContainer : function()
4187     {
4188         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4189             return this.el.select('.navbar-header',true).first();
4190         }
4191         
4192         return this.getChildContainer();
4193     },
4194     
4195     
4196     initEvents : function()
4197     {
4198         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4199         
4200         if (this.autohide) {
4201             
4202             var prevScroll = 0;
4203             var ft = this.el;
4204             
4205             Roo.get(document).on('scroll',function(e) {
4206                 var ns = Roo.get(document).getScroll().top;
4207                 var os = prevScroll;
4208                 prevScroll = ns;
4209                 
4210                 if(ns > os){
4211                     ft.removeClass('slideDown');
4212                     ft.addClass('slideUp');
4213                     return;
4214                 }
4215                 ft.removeClass('slideUp');
4216                 ft.addClass('slideDown');
4217                  
4218               
4219           },this);
4220         }
4221     }    
4222     
4223 });
4224
4225
4226
4227  
4228
4229  /*
4230  * - LGPL
4231  *
4232  * navbar
4233  * 
4234  */
4235
4236 /**
4237  * @class Roo.bootstrap.NavSidebar
4238  * @extends Roo.bootstrap.Navbar
4239  * Bootstrap Sidebar class
4240  * 
4241  * @constructor
4242  * Create a new Sidebar
4243  * @param {Object} config The config object
4244  */
4245
4246
4247 Roo.bootstrap.NavSidebar = function(config){
4248     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4249 };
4250
4251 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4252     
4253     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4254     
4255     getAutoCreate : function(){
4256         
4257         
4258         return  {
4259             tag: 'div',
4260             cls: 'sidebar sidebar-nav'
4261         };
4262     
4263         
4264     }
4265     
4266     
4267     
4268 });
4269
4270
4271
4272  
4273
4274  /*
4275  * - LGPL
4276  *
4277  * nav group
4278  * 
4279  */
4280
4281 /**
4282  * @class Roo.bootstrap.NavGroup
4283  * @extends Roo.bootstrap.Component
4284  * Bootstrap NavGroup class
4285  * @cfg {String} align (left|right)
4286  * @cfg {Boolean} inverse
4287  * @cfg {String} type (nav|pills|tab) default nav
4288  * @cfg {String} navId - reference Id for navbar.
4289
4290  * 
4291  * @constructor
4292  * Create a new nav group
4293  * @param {Object} config The config object
4294  */
4295
4296 Roo.bootstrap.NavGroup = function(config){
4297     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4298     this.navItems = [];
4299    
4300     Roo.bootstrap.NavGroup.register(this);
4301      this.addEvents({
4302         /**
4303              * @event changed
4304              * Fires when the active item changes
4305              * @param {Roo.bootstrap.NavGroup} this
4306              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4307              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4308          */
4309         'changed': true
4310      });
4311     
4312 };
4313
4314 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4315     
4316     align: '',
4317     inverse: false,
4318     form: false,
4319     type: 'nav',
4320     navId : '',
4321     // private
4322     
4323     navItems : false, 
4324     
4325     getAutoCreate : function()
4326     {
4327         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4328         
4329         cfg = {
4330             tag : 'ul',
4331             cls: 'nav' 
4332         };
4333         
4334         if (['tabs','pills'].indexOf(this.type)!==-1) {
4335             cfg.cls += ' nav-' + this.type
4336         } else {
4337             if (this.type!=='nav') {
4338                 Roo.log('nav type must be nav/tabs/pills')
4339             }
4340             cfg.cls += ' navbar-nav'
4341         }
4342         
4343         if (this.parent() && this.parent().sidebar) {
4344             cfg = {
4345                 tag: 'ul',
4346                 cls: 'dashboard-menu sidebar-menu'
4347             };
4348             
4349             return cfg;
4350         }
4351         
4352         if (this.form === true) {
4353             cfg = {
4354                 tag: 'form',
4355                 cls: 'navbar-form'
4356             };
4357             
4358             if (this.align === 'right') {
4359                 cfg.cls += ' navbar-right ml-md-auto';
4360             } else {
4361                 cfg.cls += ' navbar-left';
4362             }
4363         }
4364         
4365         if (this.align === 'right') {
4366             cfg.cls += ' navbar-right ml-md-auto';
4367         } else {
4368             cfg.cls += ' mr-auto';
4369         }
4370         
4371         if (this.inverse) {
4372             cfg.cls += ' navbar-inverse';
4373             
4374         }
4375         
4376         
4377         return cfg;
4378     },
4379     /**
4380     * sets the active Navigation item
4381     * @param {Roo.bootstrap.NavItem} the new current navitem
4382     */
4383     setActiveItem : function(item)
4384     {
4385         var prev = false;
4386         Roo.each(this.navItems, function(v){
4387             if (v == item) {
4388                 return ;
4389             }
4390             if (v.isActive()) {
4391                 v.setActive(false, true);
4392                 prev = v;
4393                 
4394             }
4395             
4396         });
4397
4398         item.setActive(true, true);
4399         this.fireEvent('changed', this, item, prev);
4400         
4401         
4402     },
4403     /**
4404     * gets the active Navigation item
4405     * @return {Roo.bootstrap.NavItem} the current navitem
4406     */
4407     getActive : function()
4408     {
4409         
4410         var prev = false;
4411         Roo.each(this.navItems, function(v){
4412             
4413             if (v.isActive()) {
4414                 prev = v;
4415                 
4416             }
4417             
4418         });
4419         return prev;
4420     },
4421     
4422     indexOfNav : function()
4423     {
4424         
4425         var prev = false;
4426         Roo.each(this.navItems, function(v,i){
4427             
4428             if (v.isActive()) {
4429                 prev = i;
4430                 
4431             }
4432             
4433         });
4434         return prev;
4435     },
4436     /**
4437     * adds a Navigation item
4438     * @param {Roo.bootstrap.NavItem} the navitem to add
4439     */
4440     addItem : function(cfg)
4441     {
4442         var cn = new Roo.bootstrap.NavItem(cfg);
4443         this.register(cn);
4444         cn.parentId = this.id;
4445         cn.onRender(this.el, null);
4446         return cn;
4447     },
4448     /**
4449     * register a Navigation item
4450     * @param {Roo.bootstrap.NavItem} the navitem to add
4451     */
4452     register : function(item)
4453     {
4454         this.navItems.push( item);
4455         item.navId = this.navId;
4456     
4457     },
4458     
4459     /**
4460     * clear all the Navigation item
4461     */
4462    
4463     clearAll : function()
4464     {
4465         this.navItems = [];
4466         this.el.dom.innerHTML = '';
4467     },
4468     
4469     getNavItem: function(tabId)
4470     {
4471         var ret = false;
4472         Roo.each(this.navItems, function(e) {
4473             if (e.tabId == tabId) {
4474                ret =  e;
4475                return false;
4476             }
4477             return true;
4478             
4479         });
4480         return ret;
4481     },
4482     
4483     setActiveNext : function()
4484     {
4485         var i = this.indexOfNav(this.getActive());
4486         if (i > this.navItems.length) {
4487             return;
4488         }
4489         this.setActiveItem(this.navItems[i+1]);
4490     },
4491     setActivePrev : function()
4492     {
4493         var i = this.indexOfNav(this.getActive());
4494         if (i  < 1) {
4495             return;
4496         }
4497         this.setActiveItem(this.navItems[i-1]);
4498     },
4499     clearWasActive : function(except) {
4500         Roo.each(this.navItems, function(e) {
4501             if (e.tabId != except.tabId && e.was_active) {
4502                e.was_active = false;
4503                return false;
4504             }
4505             return true;
4506             
4507         });
4508     },
4509     getWasActive : function ()
4510     {
4511         var r = false;
4512         Roo.each(this.navItems, function(e) {
4513             if (e.was_active) {
4514                r = e;
4515                return false;
4516             }
4517             return true;
4518             
4519         });
4520         return r;
4521     }
4522     
4523     
4524 });
4525
4526  
4527 Roo.apply(Roo.bootstrap.NavGroup, {
4528     
4529     groups: {},
4530      /**
4531     * register a Navigation Group
4532     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4533     */
4534     register : function(navgrp)
4535     {
4536         this.groups[navgrp.navId] = navgrp;
4537         
4538     },
4539     /**
4540     * fetch a Navigation Group based on the navigation ID
4541     * @param {string} the navgroup to add
4542     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4543     */
4544     get: function(navId) {
4545         if (typeof(this.groups[navId]) == 'undefined') {
4546             return false;
4547             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4548         }
4549         return this.groups[navId] ;
4550     }
4551     
4552     
4553     
4554 });
4555
4556  /*
4557  * - LGPL
4558  *
4559  * row
4560  * 
4561  */
4562
4563 /**
4564  * @class Roo.bootstrap.NavItem
4565  * @extends Roo.bootstrap.Component
4566  * Bootstrap Navbar.NavItem class
4567  * @cfg {String} href  link to
4568  * @cfg {String} html content of button
4569  * @cfg {String} badge text inside badge
4570  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4571  * @cfg {String} glyphicon DEPRICATED - use fa
4572  * @cfg {String} icon DEPRICATED - use fa
4573  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4574  * @cfg {Boolean} active Is item active
4575  * @cfg {Boolean} disabled Is item disabled
4576  
4577  * @cfg {Boolean} preventDefault (true | false) default false
4578  * @cfg {String} tabId the tab that this item activates.
4579  * @cfg {String} tagtype (a|span) render as a href or span?
4580  * @cfg {Boolean} animateRef (true|false) link to element default false  
4581   
4582  * @constructor
4583  * Create a new Navbar Item
4584  * @param {Object} config The config object
4585  */
4586 Roo.bootstrap.NavItem = function(config){
4587     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4588     this.addEvents({
4589         // raw events
4590         /**
4591          * @event click
4592          * The raw click event for the entire grid.
4593          * @param {Roo.EventObject} e
4594          */
4595         "click" : true,
4596          /**
4597             * @event changed
4598             * Fires when the active item active state changes
4599             * @param {Roo.bootstrap.NavItem} this
4600             * @param {boolean} state the new state
4601              
4602          */
4603         'changed': true,
4604         /**
4605             * @event scrollto
4606             * Fires when scroll to element
4607             * @param {Roo.bootstrap.NavItem} this
4608             * @param {Object} options
4609             * @param {Roo.EventObject} e
4610              
4611          */
4612         'scrollto': true
4613     });
4614    
4615 };
4616
4617 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4618     
4619     href: false,
4620     html: '',
4621     badge: '',
4622     icon: false,
4623     fa : false,
4624     glyphicon: false,
4625     active: false,
4626     preventDefault : false,
4627     tabId : false,
4628     tagtype : 'a',
4629     disabled : false,
4630     animateRef : false,
4631     was_active : false,
4632     
4633     getAutoCreate : function(){
4634          
4635         var cfg = {
4636             tag: 'li',
4637             cls: 'nav-item'
4638             
4639         };
4640         
4641         if (this.active) {
4642             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4643         }
4644         if (this.disabled) {
4645             cfg.cls += ' disabled';
4646         }
4647         
4648         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4649             cfg.cn = [
4650                 {
4651                     tag: this.tagtype,
4652                     href : this.href || "#",
4653                     html: this.html || ''
4654                 }
4655             ];
4656             if (this.tagtype == 'a') {
4657                 cfg.cn[0].cls = 'nav-link';
4658             }
4659             if (this.icon) {
4660                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4661             }
4662             if (this.fa) {
4663                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4664             }
4665             if(this.glyphicon) {
4666                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4667             }
4668             
4669             if (this.menu) {
4670                 
4671                 cfg.cn[0].html += " <span class='caret'></span>";
4672              
4673             }
4674             
4675             if (this.badge !== '') {
4676                  
4677                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4678             }
4679         }
4680         
4681         
4682         
4683         return cfg;
4684     },
4685     initEvents: function() 
4686     {
4687         if (typeof (this.menu) != 'undefined') {
4688             this.menu.parentType = this.xtype;
4689             this.menu.triggerEl = this.el;
4690             this.menu = this.addxtype(Roo.apply({}, this.menu));
4691         }
4692         
4693         this.el.select('a',true).on('click', this.onClick, this);
4694         
4695         if(this.tagtype == 'span'){
4696             this.el.select('span',true).on('click', this.onClick, this);
4697         }
4698        
4699         // at this point parent should be available..
4700         this.parent().register(this);
4701     },
4702     
4703     onClick : function(e)
4704     {
4705         if (e.getTarget('.dropdown-menu-item')) {
4706             // did you click on a menu itemm.... - then don't trigger onclick..
4707             return;
4708         }
4709         
4710         if(
4711                 this.preventDefault || 
4712                 this.href == '#' 
4713         ){
4714             Roo.log("NavItem - prevent Default?");
4715             e.preventDefault();
4716         }
4717         
4718         if (this.disabled) {
4719             return;
4720         }
4721         
4722         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4723         if (tg && tg.transition) {
4724             Roo.log("waiting for the transitionend");
4725             return;
4726         }
4727         
4728         
4729         
4730         //Roo.log("fire event clicked");
4731         if(this.fireEvent('click', this, e) === false){
4732             return;
4733         };
4734         
4735         if(this.tagtype == 'span'){
4736             return;
4737         }
4738         
4739         //Roo.log(this.href);
4740         var ael = this.el.select('a',true).first();
4741         //Roo.log(ael);
4742         
4743         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4744             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4745             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4746                 return; // ignore... - it's a 'hash' to another page.
4747             }
4748             Roo.log("NavItem - prevent Default?");
4749             e.preventDefault();
4750             this.scrollToElement(e);
4751         }
4752         
4753         
4754         var p =  this.parent();
4755    
4756         if (['tabs','pills'].indexOf(p.type)!==-1) {
4757             if (typeof(p.setActiveItem) !== 'undefined') {
4758                 p.setActiveItem(this);
4759             }
4760         }
4761         
4762         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4763         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4764             // remove the collapsed menu expand...
4765             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4766         }
4767     },
4768     
4769     isActive: function () {
4770         return this.active
4771     },
4772     setActive : function(state, fire, is_was_active)
4773     {
4774         if (this.active && !state && this.navId) {
4775             this.was_active = true;
4776             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4777             if (nv) {
4778                 nv.clearWasActive(this);
4779             }
4780             
4781         }
4782         this.active = state;
4783         
4784         if (!state ) {
4785             this.el.removeClass('active');
4786         } else if (!this.el.hasClass('active')) {
4787             this.el.addClass('active');
4788         }
4789         if (fire) {
4790             this.fireEvent('changed', this, state);
4791         }
4792         
4793         // show a panel if it's registered and related..
4794         
4795         if (!this.navId || !this.tabId || !state || is_was_active) {
4796             return;
4797         }
4798         
4799         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4800         if (!tg) {
4801             return;
4802         }
4803         var pan = tg.getPanelByName(this.tabId);
4804         if (!pan) {
4805             return;
4806         }
4807         // if we can not flip to new panel - go back to old nav highlight..
4808         if (false == tg.showPanel(pan)) {
4809             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4810             if (nv) {
4811                 var onav = nv.getWasActive();
4812                 if (onav) {
4813                     onav.setActive(true, false, true);
4814                 }
4815             }
4816             
4817         }
4818         
4819         
4820         
4821     },
4822      // this should not be here...
4823     setDisabled : function(state)
4824     {
4825         this.disabled = state;
4826         if (!state ) {
4827             this.el.removeClass('disabled');
4828         } else if (!this.el.hasClass('disabled')) {
4829             this.el.addClass('disabled');
4830         }
4831         
4832     },
4833     
4834     /**
4835      * Fetch the element to display the tooltip on.
4836      * @return {Roo.Element} defaults to this.el
4837      */
4838     tooltipEl : function()
4839     {
4840         return this.el.select('' + this.tagtype + '', true).first();
4841     },
4842     
4843     scrollToElement : function(e)
4844     {
4845         var c = document.body;
4846         
4847         /*
4848          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4849          */
4850         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4851             c = document.documentElement;
4852         }
4853         
4854         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4855         
4856         if(!target){
4857             return;
4858         }
4859
4860         var o = target.calcOffsetsTo(c);
4861         
4862         var options = {
4863             target : target,
4864             value : o[1]
4865         };
4866         
4867         this.fireEvent('scrollto', this, options, e);
4868         
4869         Roo.get(c).scrollTo('top', options.value, true);
4870         
4871         return;
4872     }
4873 });
4874  
4875
4876  /*
4877  * - LGPL
4878  *
4879  * sidebar item
4880  *
4881  *  li
4882  *    <span> icon </span>
4883  *    <span> text </span>
4884  *    <span>badge </span>
4885  */
4886
4887 /**
4888  * @class Roo.bootstrap.NavSidebarItem
4889  * @extends Roo.bootstrap.NavItem
4890  * Bootstrap Navbar.NavSidebarItem class
4891  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4892  * {Boolean} open is the menu open
4893  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4894  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4895  * {String} buttonSize (sm|md|lg)the extra classes for the button
4896  * {Boolean} showArrow show arrow next to the text (default true)
4897  * @constructor
4898  * Create a new Navbar Button
4899  * @param {Object} config The config object
4900  */
4901 Roo.bootstrap.NavSidebarItem = function(config){
4902     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4903     this.addEvents({
4904         // raw events
4905         /**
4906          * @event click
4907          * The raw click event for the entire grid.
4908          * @param {Roo.EventObject} e
4909          */
4910         "click" : true,
4911          /**
4912             * @event changed
4913             * Fires when the active item active state changes
4914             * @param {Roo.bootstrap.NavSidebarItem} this
4915             * @param {boolean} state the new state
4916              
4917          */
4918         'changed': true
4919     });
4920    
4921 };
4922
4923 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4924     
4925     badgeWeight : 'default',
4926     
4927     open: false,
4928     
4929     buttonView : false,
4930     
4931     buttonWeight : 'default',
4932     
4933     buttonSize : 'md',
4934     
4935     showArrow : true,
4936     
4937     getAutoCreate : function(){
4938         
4939         
4940         var a = {
4941                 tag: 'a',
4942                 href : this.href || '#',
4943                 cls: '',
4944                 html : '',
4945                 cn : []
4946         };
4947         
4948         if(this.buttonView){
4949             a = {
4950                 tag: 'button',
4951                 href : this.href || '#',
4952                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4953                 html : this.html,
4954                 cn : []
4955             };
4956         }
4957         
4958         var cfg = {
4959             tag: 'li',
4960             cls: '',
4961             cn: [ a ]
4962         };
4963         
4964         if (this.active) {
4965             cfg.cls += ' active';
4966         }
4967         
4968         if (this.disabled) {
4969             cfg.cls += ' disabled';
4970         }
4971         if (this.open) {
4972             cfg.cls += ' open x-open';
4973         }
4974         // left icon..
4975         if (this.glyphicon || this.icon) {
4976             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4977             a.cn.push({ tag : 'i', cls : c }) ;
4978         }
4979         
4980         if(!this.buttonView){
4981             var span = {
4982                 tag: 'span',
4983                 html : this.html || ''
4984             };
4985
4986             a.cn.push(span);
4987             
4988         }
4989         
4990         if (this.badge !== '') {
4991             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4992         }
4993         
4994         if (this.menu) {
4995             
4996             if(this.showArrow){
4997                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4998             }
4999             
5000             a.cls += ' dropdown-toggle treeview' ;
5001         }
5002         
5003         return cfg;
5004     },
5005     
5006     initEvents : function()
5007     { 
5008         if (typeof (this.menu) != 'undefined') {
5009             this.menu.parentType = this.xtype;
5010             this.menu.triggerEl = this.el;
5011             this.menu = this.addxtype(Roo.apply({}, this.menu));
5012         }
5013         
5014         this.el.on('click', this.onClick, this);
5015         
5016         if(this.badge !== ''){
5017             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5018         }
5019         
5020     },
5021     
5022     onClick : function(e)
5023     {
5024         if(this.disabled){
5025             e.preventDefault();
5026             return;
5027         }
5028         
5029         if(this.preventDefault){
5030             e.preventDefault();
5031         }
5032         
5033         this.fireEvent('click', this);
5034     },
5035     
5036     disable : function()
5037     {
5038         this.setDisabled(true);
5039     },
5040     
5041     enable : function()
5042     {
5043         this.setDisabled(false);
5044     },
5045     
5046     setDisabled : function(state)
5047     {
5048         if(this.disabled == state){
5049             return;
5050         }
5051         
5052         this.disabled = state;
5053         
5054         if (state) {
5055             this.el.addClass('disabled');
5056             return;
5057         }
5058         
5059         this.el.removeClass('disabled');
5060         
5061         return;
5062     },
5063     
5064     setActive : function(state)
5065     {
5066         if(this.active == state){
5067             return;
5068         }
5069         
5070         this.active = state;
5071         
5072         if (state) {
5073             this.el.addClass('active');
5074             return;
5075         }
5076         
5077         this.el.removeClass('active');
5078         
5079         return;
5080     },
5081     
5082     isActive: function () 
5083     {
5084         return this.active;
5085     },
5086     
5087     setBadge : function(str)
5088     {
5089         if(!this.badgeEl){
5090             return;
5091         }
5092         
5093         this.badgeEl.dom.innerHTML = str;
5094     }
5095     
5096    
5097      
5098  
5099 });
5100  
5101
5102  /*
5103  * - LGPL
5104  *
5105  * row
5106  * 
5107  */
5108
5109 /**
5110  * @class Roo.bootstrap.Row
5111  * @extends Roo.bootstrap.Component
5112  * Bootstrap Row class (contains columns...)
5113  * 
5114  * @constructor
5115  * Create a new Row
5116  * @param {Object} config The config object
5117  */
5118
5119 Roo.bootstrap.Row = function(config){
5120     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5121 };
5122
5123 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5124     
5125     getAutoCreate : function(){
5126        return {
5127             cls: 'row clearfix'
5128        };
5129     }
5130     
5131     
5132 });
5133
5134  
5135
5136  /*
5137  * - LGPL
5138  *
5139  * element
5140  * 
5141  */
5142
5143 /**
5144  * @class Roo.bootstrap.Element
5145  * @extends Roo.bootstrap.Component
5146  * Bootstrap Element class
5147  * @cfg {String} html contents of the element
5148  * @cfg {String} tag tag of the element
5149  * @cfg {String} cls class of the element
5150  * @cfg {Boolean} preventDefault (true|false) default false
5151  * @cfg {Boolean} clickable (true|false) default false
5152  * 
5153  * @constructor
5154  * Create a new Element
5155  * @param {Object} config The config object
5156  */
5157
5158 Roo.bootstrap.Element = function(config){
5159     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5160     
5161     this.addEvents({
5162         // raw events
5163         /**
5164          * @event click
5165          * When a element is chick
5166          * @param {Roo.bootstrap.Element} this
5167          * @param {Roo.EventObject} e
5168          */
5169         "click" : true
5170     });
5171 };
5172
5173 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5174     
5175     tag: 'div',
5176     cls: '',
5177     html: '',
5178     preventDefault: false, 
5179     clickable: false,
5180     
5181     getAutoCreate : function(){
5182         
5183         var cfg = {
5184             tag: this.tag,
5185             // cls: this.cls, double assign in parent class Component.js :: onRender
5186             html: this.html
5187         };
5188         
5189         return cfg;
5190     },
5191     
5192     initEvents: function() 
5193     {
5194         Roo.bootstrap.Element.superclass.initEvents.call(this);
5195         
5196         if(this.clickable){
5197             this.el.on('click', this.onClick, this);
5198         }
5199         
5200     },
5201     
5202     onClick : function(e)
5203     {
5204         if(this.preventDefault){
5205             e.preventDefault();
5206         }
5207         
5208         this.fireEvent('click', this, e);
5209     },
5210     
5211     getValue : function()
5212     {
5213         return this.el.dom.innerHTML;
5214     },
5215     
5216     setValue : function(value)
5217     {
5218         this.el.dom.innerHTML = value;
5219     }
5220    
5221 });
5222
5223  
5224
5225  /*
5226  * - LGPL
5227  *
5228  * pagination
5229  * 
5230  */
5231
5232 /**
5233  * @class Roo.bootstrap.Pagination
5234  * @extends Roo.bootstrap.Component
5235  * Bootstrap Pagination class
5236  * @cfg {String} size xs | sm | md | lg
5237  * @cfg {Boolean} inverse false | true
5238  * 
5239  * @constructor
5240  * Create a new Pagination
5241  * @param {Object} config The config object
5242  */
5243
5244 Roo.bootstrap.Pagination = function(config){
5245     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5246 };
5247
5248 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5249     
5250     cls: false,
5251     size: false,
5252     inverse: false,
5253     
5254     getAutoCreate : function(){
5255         var cfg = {
5256             tag: 'ul',
5257                 cls: 'pagination'
5258         };
5259         if (this.inverse) {
5260             cfg.cls += ' inverse';
5261         }
5262         if (this.html) {
5263             cfg.html=this.html;
5264         }
5265         if (this.cls) {
5266             cfg.cls += " " + this.cls;
5267         }
5268         return cfg;
5269     }
5270    
5271 });
5272
5273  
5274
5275  /*
5276  * - LGPL
5277  *
5278  * Pagination item
5279  * 
5280  */
5281
5282
5283 /**
5284  * @class Roo.bootstrap.PaginationItem
5285  * @extends Roo.bootstrap.Component
5286  * Bootstrap PaginationItem class
5287  * @cfg {String} html text
5288  * @cfg {String} href the link
5289  * @cfg {Boolean} preventDefault (true | false) default true
5290  * @cfg {Boolean} active (true | false) default false
5291  * @cfg {Boolean} disabled default false
5292  * 
5293  * 
5294  * @constructor
5295  * Create a new PaginationItem
5296  * @param {Object} config The config object
5297  */
5298
5299
5300 Roo.bootstrap.PaginationItem = function(config){
5301     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5302     this.addEvents({
5303         // raw events
5304         /**
5305          * @event click
5306          * The raw click event for the entire grid.
5307          * @param {Roo.EventObject} e
5308          */
5309         "click" : true
5310     });
5311 };
5312
5313 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5314     
5315     href : false,
5316     html : false,
5317     preventDefault: true,
5318     active : false,
5319     cls : false,
5320     disabled: false,
5321     
5322     getAutoCreate : function(){
5323         var cfg= {
5324             tag: 'li',
5325             cn: [
5326                 {
5327                     tag : 'a',
5328                     href : this.href ? this.href : '#',
5329                     html : this.html ? this.html : ''
5330                 }
5331             ]
5332         };
5333         
5334         if(this.cls){
5335             cfg.cls = this.cls;
5336         }
5337         
5338         if(this.disabled){
5339             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5340         }
5341         
5342         if(this.active){
5343             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5344         }
5345         
5346         return cfg;
5347     },
5348     
5349     initEvents: function() {
5350         
5351         this.el.on('click', this.onClick, this);
5352         
5353     },
5354     onClick : function(e)
5355     {
5356         Roo.log('PaginationItem on click ');
5357         if(this.preventDefault){
5358             e.preventDefault();
5359         }
5360         
5361         if(this.disabled){
5362             return;
5363         }
5364         
5365         this.fireEvent('click', this, e);
5366     }
5367    
5368 });
5369
5370  
5371
5372  /*
5373  * - LGPL
5374  *
5375  * slider
5376  * 
5377  */
5378
5379
5380 /**
5381  * @class Roo.bootstrap.Slider
5382  * @extends Roo.bootstrap.Component
5383  * Bootstrap Slider class
5384  *    
5385  * @constructor
5386  * Create a new Slider
5387  * @param {Object} config The config object
5388  */
5389
5390 Roo.bootstrap.Slider = function(config){
5391     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5392 };
5393
5394 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5395     
5396     getAutoCreate : function(){
5397         
5398         var cfg = {
5399             tag: 'div',
5400             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5401             cn: [
5402                 {
5403                     tag: 'a',
5404                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5405                 }
5406             ]
5407         };
5408         
5409         return cfg;
5410     }
5411    
5412 });
5413
5414  /*
5415  * Based on:
5416  * Ext JS Library 1.1.1
5417  * Copyright(c) 2006-2007, Ext JS, LLC.
5418  *
5419  * Originally Released Under LGPL - original licence link has changed is not relivant.
5420  *
5421  * Fork - LGPL
5422  * <script type="text/javascript">
5423  */
5424  
5425
5426 /**
5427  * @class Roo.grid.ColumnModel
5428  * @extends Roo.util.Observable
5429  * This is the default implementation of a ColumnModel used by the Grid. It defines
5430  * the columns in the grid.
5431  * <br>Usage:<br>
5432  <pre><code>
5433  var colModel = new Roo.grid.ColumnModel([
5434         {header: "Ticker", width: 60, sortable: true, locked: true},
5435         {header: "Company Name", width: 150, sortable: true},
5436         {header: "Market Cap.", width: 100, sortable: true},
5437         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5438         {header: "Employees", width: 100, sortable: true, resizable: false}
5439  ]);
5440  </code></pre>
5441  * <p>
5442  
5443  * The config options listed for this class are options which may appear in each
5444  * individual column definition.
5445  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5446  * @constructor
5447  * @param {Object} config An Array of column config objects. See this class's
5448  * config objects for details.
5449 */
5450 Roo.grid.ColumnModel = function(config){
5451         /**
5452      * The config passed into the constructor
5453      */
5454     this.config = config;
5455     this.lookup = {};
5456
5457     // if no id, create one
5458     // if the column does not have a dataIndex mapping,
5459     // map it to the order it is in the config
5460     for(var i = 0, len = config.length; i < len; i++){
5461         var c = config[i];
5462         if(typeof c.dataIndex == "undefined"){
5463             c.dataIndex = i;
5464         }
5465         if(typeof c.renderer == "string"){
5466             c.renderer = Roo.util.Format[c.renderer];
5467         }
5468         if(typeof c.id == "undefined"){
5469             c.id = Roo.id();
5470         }
5471         if(c.editor && c.editor.xtype){
5472             c.editor  = Roo.factory(c.editor, Roo.grid);
5473         }
5474         if(c.editor && c.editor.isFormField){
5475             c.editor = new Roo.grid.GridEditor(c.editor);
5476         }
5477         this.lookup[c.id] = c;
5478     }
5479
5480     /**
5481      * The width of columns which have no width specified (defaults to 100)
5482      * @type Number
5483      */
5484     this.defaultWidth = 100;
5485
5486     /**
5487      * Default sortable of columns which have no sortable specified (defaults to false)
5488      * @type Boolean
5489      */
5490     this.defaultSortable = false;
5491
5492     this.addEvents({
5493         /**
5494              * @event widthchange
5495              * Fires when the width of a column changes.
5496              * @param {ColumnModel} this
5497              * @param {Number} columnIndex The column index
5498              * @param {Number} newWidth The new width
5499              */
5500             "widthchange": true,
5501         /**
5502              * @event headerchange
5503              * Fires when the text of a header changes.
5504              * @param {ColumnModel} this
5505              * @param {Number} columnIndex The column index
5506              * @param {Number} newText The new header text
5507              */
5508             "headerchange": true,
5509         /**
5510              * @event hiddenchange
5511              * Fires when a column is hidden or "unhidden".
5512              * @param {ColumnModel} this
5513              * @param {Number} columnIndex The column index
5514              * @param {Boolean} hidden true if hidden, false otherwise
5515              */
5516             "hiddenchange": true,
5517             /**
5518          * @event columnmoved
5519          * Fires when a column is moved.
5520          * @param {ColumnModel} this
5521          * @param {Number} oldIndex
5522          * @param {Number} newIndex
5523          */
5524         "columnmoved" : true,
5525         /**
5526          * @event columlockchange
5527          * Fires when a column's locked state is changed
5528          * @param {ColumnModel} this
5529          * @param {Number} colIndex
5530          * @param {Boolean} locked true if locked
5531          */
5532         "columnlockchange" : true
5533     });
5534     Roo.grid.ColumnModel.superclass.constructor.call(this);
5535 };
5536 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5537     /**
5538      * @cfg {String} header The header text to display in the Grid view.
5539      */
5540     /**
5541      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5542      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5543      * specified, the column's index is used as an index into the Record's data Array.
5544      */
5545     /**
5546      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5547      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5548      */
5549     /**
5550      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5551      * Defaults to the value of the {@link #defaultSortable} property.
5552      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5553      */
5554     /**
5555      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5556      */
5557     /**
5558      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5559      */
5560     /**
5561      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5562      */
5563     /**
5564      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5565      */
5566     /**
5567      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5568      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5569      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5570      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5571      */
5572        /**
5573      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5574      */
5575     /**
5576      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5577      */
5578     /**
5579      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5580      */
5581     /**
5582      * @cfg {String} cursor (Optional)
5583      */
5584     /**
5585      * @cfg {String} tooltip (Optional)
5586      */
5587     /**
5588      * @cfg {Number} xs (Optional)
5589      */
5590     /**
5591      * @cfg {Number} sm (Optional)
5592      */
5593     /**
5594      * @cfg {Number} md (Optional)
5595      */
5596     /**
5597      * @cfg {Number} lg (Optional)
5598      */
5599     /**
5600      * Returns the id of the column at the specified index.
5601      * @param {Number} index The column index
5602      * @return {String} the id
5603      */
5604     getColumnId : function(index){
5605         return this.config[index].id;
5606     },
5607
5608     /**
5609      * Returns the column for a specified id.
5610      * @param {String} id The column id
5611      * @return {Object} the column
5612      */
5613     getColumnById : function(id){
5614         return this.lookup[id];
5615     },
5616
5617     
5618     /**
5619      * Returns the column for a specified dataIndex.
5620      * @param {String} dataIndex The column dataIndex
5621      * @return {Object|Boolean} the column or false if not found
5622      */
5623     getColumnByDataIndex: function(dataIndex){
5624         var index = this.findColumnIndex(dataIndex);
5625         return index > -1 ? this.config[index] : false;
5626     },
5627     
5628     /**
5629      * Returns the index for a specified column id.
5630      * @param {String} id The column id
5631      * @return {Number} the index, or -1 if not found
5632      */
5633     getIndexById : function(id){
5634         for(var i = 0, len = this.config.length; i < len; i++){
5635             if(this.config[i].id == id){
5636                 return i;
5637             }
5638         }
5639         return -1;
5640     },
5641     
5642     /**
5643      * Returns the index for a specified column dataIndex.
5644      * @param {String} dataIndex The column dataIndex
5645      * @return {Number} the index, or -1 if not found
5646      */
5647     
5648     findColumnIndex : function(dataIndex){
5649         for(var i = 0, len = this.config.length; i < len; i++){
5650             if(this.config[i].dataIndex == dataIndex){
5651                 return i;
5652             }
5653         }
5654         return -1;
5655     },
5656     
5657     
5658     moveColumn : function(oldIndex, newIndex){
5659         var c = this.config[oldIndex];
5660         this.config.splice(oldIndex, 1);
5661         this.config.splice(newIndex, 0, c);
5662         this.dataMap = null;
5663         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5664     },
5665
5666     isLocked : function(colIndex){
5667         return this.config[colIndex].locked === true;
5668     },
5669
5670     setLocked : function(colIndex, value, suppressEvent){
5671         if(this.isLocked(colIndex) == value){
5672             return;
5673         }
5674         this.config[colIndex].locked = value;
5675         if(!suppressEvent){
5676             this.fireEvent("columnlockchange", this, colIndex, value);
5677         }
5678     },
5679
5680     getTotalLockedWidth : function(){
5681         var totalWidth = 0;
5682         for(var i = 0; i < this.config.length; i++){
5683             if(this.isLocked(i) && !this.isHidden(i)){
5684                 this.totalWidth += this.getColumnWidth(i);
5685             }
5686         }
5687         return totalWidth;
5688     },
5689
5690     getLockedCount : function(){
5691         for(var i = 0, len = this.config.length; i < len; i++){
5692             if(!this.isLocked(i)){
5693                 return i;
5694             }
5695         }
5696         
5697         return this.config.length;
5698     },
5699
5700     /**
5701      * Returns the number of columns.
5702      * @return {Number}
5703      */
5704     getColumnCount : function(visibleOnly){
5705         if(visibleOnly === true){
5706             var c = 0;
5707             for(var i = 0, len = this.config.length; i < len; i++){
5708                 if(!this.isHidden(i)){
5709                     c++;
5710                 }
5711             }
5712             return c;
5713         }
5714         return this.config.length;
5715     },
5716
5717     /**
5718      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5719      * @param {Function} fn
5720      * @param {Object} scope (optional)
5721      * @return {Array} result
5722      */
5723     getColumnsBy : function(fn, scope){
5724         var r = [];
5725         for(var i = 0, len = this.config.length; i < len; i++){
5726             var c = this.config[i];
5727             if(fn.call(scope||this, c, i) === true){
5728                 r[r.length] = c;
5729             }
5730         }
5731         return r;
5732     },
5733
5734     /**
5735      * Returns true if the specified column is sortable.
5736      * @param {Number} col The column index
5737      * @return {Boolean}
5738      */
5739     isSortable : function(col){
5740         if(typeof this.config[col].sortable == "undefined"){
5741             return this.defaultSortable;
5742         }
5743         return this.config[col].sortable;
5744     },
5745
5746     /**
5747      * Returns the rendering (formatting) function defined for the column.
5748      * @param {Number} col The column index.
5749      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5750      */
5751     getRenderer : function(col){
5752         if(!this.config[col].renderer){
5753             return Roo.grid.ColumnModel.defaultRenderer;
5754         }
5755         return this.config[col].renderer;
5756     },
5757
5758     /**
5759      * Sets the rendering (formatting) function for a column.
5760      * @param {Number} col The column index
5761      * @param {Function} fn The function to use to process the cell's raw data
5762      * to return HTML markup for the grid view. The render function is called with
5763      * the following parameters:<ul>
5764      * <li>Data value.</li>
5765      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5766      * <li>css A CSS style string to apply to the table cell.</li>
5767      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5768      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5769      * <li>Row index</li>
5770      * <li>Column index</li>
5771      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5772      */
5773     setRenderer : function(col, fn){
5774         this.config[col].renderer = fn;
5775     },
5776
5777     /**
5778      * Returns the width for the specified column.
5779      * @param {Number} col The column index
5780      * @return {Number}
5781      */
5782     getColumnWidth : function(col){
5783         return this.config[col].width * 1 || this.defaultWidth;
5784     },
5785
5786     /**
5787      * Sets the width for a column.
5788      * @param {Number} col The column index
5789      * @param {Number} width The new width
5790      */
5791     setColumnWidth : function(col, width, suppressEvent){
5792         this.config[col].width = width;
5793         this.totalWidth = null;
5794         if(!suppressEvent){
5795              this.fireEvent("widthchange", this, col, width);
5796         }
5797     },
5798
5799     /**
5800      * Returns the total width of all columns.
5801      * @param {Boolean} includeHidden True to include hidden column widths
5802      * @return {Number}
5803      */
5804     getTotalWidth : function(includeHidden){
5805         if(!this.totalWidth){
5806             this.totalWidth = 0;
5807             for(var i = 0, len = this.config.length; i < len; i++){
5808                 if(includeHidden || !this.isHidden(i)){
5809                     this.totalWidth += this.getColumnWidth(i);
5810                 }
5811             }
5812         }
5813         return this.totalWidth;
5814     },
5815
5816     /**
5817      * Returns the header for the specified column.
5818      * @param {Number} col The column index
5819      * @return {String}
5820      */
5821     getColumnHeader : function(col){
5822         return this.config[col].header;
5823     },
5824
5825     /**
5826      * Sets the header for a column.
5827      * @param {Number} col The column index
5828      * @param {String} header The new header
5829      */
5830     setColumnHeader : function(col, header){
5831         this.config[col].header = header;
5832         this.fireEvent("headerchange", this, col, header);
5833     },
5834
5835     /**
5836      * Returns the tooltip for the specified column.
5837      * @param {Number} col The column index
5838      * @return {String}
5839      */
5840     getColumnTooltip : function(col){
5841             return this.config[col].tooltip;
5842     },
5843     /**
5844      * Sets the tooltip for a column.
5845      * @param {Number} col The column index
5846      * @param {String} tooltip The new tooltip
5847      */
5848     setColumnTooltip : function(col, tooltip){
5849             this.config[col].tooltip = tooltip;
5850     },
5851
5852     /**
5853      * Returns the dataIndex for the specified column.
5854      * @param {Number} col The column index
5855      * @return {Number}
5856      */
5857     getDataIndex : function(col){
5858         return this.config[col].dataIndex;
5859     },
5860
5861     /**
5862      * Sets the dataIndex for a column.
5863      * @param {Number} col The column index
5864      * @param {Number} dataIndex The new dataIndex
5865      */
5866     setDataIndex : function(col, dataIndex){
5867         this.config[col].dataIndex = dataIndex;
5868     },
5869
5870     
5871     
5872     /**
5873      * Returns true if the cell is editable.
5874      * @param {Number} colIndex The column index
5875      * @param {Number} rowIndex The row index - this is nto actually used..?
5876      * @return {Boolean}
5877      */
5878     isCellEditable : function(colIndex, rowIndex){
5879         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5880     },
5881
5882     /**
5883      * Returns the editor defined for the cell/column.
5884      * return false or null to disable editing.
5885      * @param {Number} colIndex The column index
5886      * @param {Number} rowIndex The row index
5887      * @return {Object}
5888      */
5889     getCellEditor : function(colIndex, rowIndex){
5890         return this.config[colIndex].editor;
5891     },
5892
5893     /**
5894      * Sets if a column is editable.
5895      * @param {Number} col The column index
5896      * @param {Boolean} editable True if the column is editable
5897      */
5898     setEditable : function(col, editable){
5899         this.config[col].editable = editable;
5900     },
5901
5902
5903     /**
5904      * Returns true if the column is hidden.
5905      * @param {Number} colIndex The column index
5906      * @return {Boolean}
5907      */
5908     isHidden : function(colIndex){
5909         return this.config[colIndex].hidden;
5910     },
5911
5912
5913     /**
5914      * Returns true if the column width cannot be changed
5915      */
5916     isFixed : function(colIndex){
5917         return this.config[colIndex].fixed;
5918     },
5919
5920     /**
5921      * Returns true if the column can be resized
5922      * @return {Boolean}
5923      */
5924     isResizable : function(colIndex){
5925         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5926     },
5927     /**
5928      * Sets if a column is hidden.
5929      * @param {Number} colIndex The column index
5930      * @param {Boolean} hidden True if the column is hidden
5931      */
5932     setHidden : function(colIndex, hidden){
5933         this.config[colIndex].hidden = hidden;
5934         this.totalWidth = null;
5935         this.fireEvent("hiddenchange", this, colIndex, hidden);
5936     },
5937
5938     /**
5939      * Sets the editor for a column.
5940      * @param {Number} col The column index
5941      * @param {Object} editor The editor object
5942      */
5943     setEditor : function(col, editor){
5944         this.config[col].editor = editor;
5945     }
5946 });
5947
5948 Roo.grid.ColumnModel.defaultRenderer = function(value)
5949 {
5950     if(typeof value == "object") {
5951         return value;
5952     }
5953         if(typeof value == "string" && value.length < 1){
5954             return "&#160;";
5955         }
5956     
5957         return String.format("{0}", value);
5958 };
5959
5960 // Alias for backwards compatibility
5961 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5962 /*
5963  * Based on:
5964  * Ext JS Library 1.1.1
5965  * Copyright(c) 2006-2007, Ext JS, LLC.
5966  *
5967  * Originally Released Under LGPL - original licence link has changed is not relivant.
5968  *
5969  * Fork - LGPL
5970  * <script type="text/javascript">
5971  */
5972  
5973 /**
5974  * @class Roo.LoadMask
5975  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5976  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5977  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5978  * element's UpdateManager load indicator and will be destroyed after the initial load.
5979  * @constructor
5980  * Create a new LoadMask
5981  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5982  * @param {Object} config The config object
5983  */
5984 Roo.LoadMask = function(el, config){
5985     this.el = Roo.get(el);
5986     Roo.apply(this, config);
5987     if(this.store){
5988         this.store.on('beforeload', this.onBeforeLoad, this);
5989         this.store.on('load', this.onLoad, this);
5990         this.store.on('loadexception', this.onLoadException, this);
5991         this.removeMask = false;
5992     }else{
5993         var um = this.el.getUpdateManager();
5994         um.showLoadIndicator = false; // disable the default indicator
5995         um.on('beforeupdate', this.onBeforeLoad, this);
5996         um.on('update', this.onLoad, this);
5997         um.on('failure', this.onLoad, this);
5998         this.removeMask = true;
5999     }
6000 };
6001
6002 Roo.LoadMask.prototype = {
6003     /**
6004      * @cfg {Boolean} removeMask
6005      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6006      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6007      */
6008     /**
6009      * @cfg {String} msg
6010      * The text to display in a centered loading message box (defaults to 'Loading...')
6011      */
6012     msg : 'Loading...',
6013     /**
6014      * @cfg {String} msgCls
6015      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6016      */
6017     msgCls : 'x-mask-loading',
6018
6019     /**
6020      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6021      * @type Boolean
6022      */
6023     disabled: false,
6024
6025     /**
6026      * Disables the mask to prevent it from being displayed
6027      */
6028     disable : function(){
6029        this.disabled = true;
6030     },
6031
6032     /**
6033      * Enables the mask so that it can be displayed
6034      */
6035     enable : function(){
6036         this.disabled = false;
6037     },
6038     
6039     onLoadException : function()
6040     {
6041         Roo.log(arguments);
6042         
6043         if (typeof(arguments[3]) != 'undefined') {
6044             Roo.MessageBox.alert("Error loading",arguments[3]);
6045         } 
6046         /*
6047         try {
6048             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6049                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6050             }   
6051         } catch(e) {
6052             
6053         }
6054         */
6055     
6056         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6057     },
6058     // private
6059     onLoad : function()
6060     {
6061         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6062     },
6063
6064     // private
6065     onBeforeLoad : function(){
6066         if(!this.disabled){
6067             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6068         }
6069     },
6070
6071     // private
6072     destroy : function(){
6073         if(this.store){
6074             this.store.un('beforeload', this.onBeforeLoad, this);
6075             this.store.un('load', this.onLoad, this);
6076             this.store.un('loadexception', this.onLoadException, this);
6077         }else{
6078             var um = this.el.getUpdateManager();
6079             um.un('beforeupdate', this.onBeforeLoad, this);
6080             um.un('update', this.onLoad, this);
6081             um.un('failure', this.onLoad, this);
6082         }
6083     }
6084 };/*
6085  * - LGPL
6086  *
6087  * table
6088  * 
6089  */
6090
6091 /**
6092  * @class Roo.bootstrap.Table
6093  * @extends Roo.bootstrap.Component
6094  * Bootstrap Table class
6095  * @cfg {String} cls table class
6096  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6097  * @cfg {String} bgcolor Specifies the background color for a table
6098  * @cfg {Number} border Specifies whether the table cells should have borders or not
6099  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6100  * @cfg {Number} cellspacing Specifies the space between cells
6101  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6102  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6103  * @cfg {String} sortable Specifies that the table should be sortable
6104  * @cfg {String} summary Specifies a summary of the content of a table
6105  * @cfg {Number} width Specifies the width of a table
6106  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6107  * 
6108  * @cfg {boolean} striped Should the rows be alternative striped
6109  * @cfg {boolean} bordered Add borders to the table
6110  * @cfg {boolean} hover Add hover highlighting
6111  * @cfg {boolean} condensed Format condensed
6112  * @cfg {boolean} responsive Format condensed
6113  * @cfg {Boolean} loadMask (true|false) default false
6114  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6115  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6116  * @cfg {Boolean} rowSelection (true|false) default false
6117  * @cfg {Boolean} cellSelection (true|false) default false
6118  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6119  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6120  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6121  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6122  
6123  * 
6124  * @constructor
6125  * Create a new Table
6126  * @param {Object} config The config object
6127  */
6128
6129 Roo.bootstrap.Table = function(config){
6130     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6131     
6132   
6133     
6134     // BC...
6135     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6136     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6137     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6138     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6139     
6140     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6141     if (this.sm) {
6142         this.sm.grid = this;
6143         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6144         this.sm = this.selModel;
6145         this.sm.xmodule = this.xmodule || false;
6146     }
6147     
6148     if (this.cm && typeof(this.cm.config) == 'undefined') {
6149         this.colModel = new Roo.grid.ColumnModel(this.cm);
6150         this.cm = this.colModel;
6151         this.cm.xmodule = this.xmodule || false;
6152     }
6153     if (this.store) {
6154         this.store= Roo.factory(this.store, Roo.data);
6155         this.ds = this.store;
6156         this.ds.xmodule = this.xmodule || false;
6157          
6158     }
6159     if (this.footer && this.store) {
6160         this.footer.dataSource = this.ds;
6161         this.footer = Roo.factory(this.footer);
6162     }
6163     
6164     /** @private */
6165     this.addEvents({
6166         /**
6167          * @event cellclick
6168          * Fires when a cell is clicked
6169          * @param {Roo.bootstrap.Table} this
6170          * @param {Roo.Element} el
6171          * @param {Number} rowIndex
6172          * @param {Number} columnIndex
6173          * @param {Roo.EventObject} e
6174          */
6175         "cellclick" : true,
6176         /**
6177          * @event celldblclick
6178          * Fires when a cell is double clicked
6179          * @param {Roo.bootstrap.Table} this
6180          * @param {Roo.Element} el
6181          * @param {Number} rowIndex
6182          * @param {Number} columnIndex
6183          * @param {Roo.EventObject} e
6184          */
6185         "celldblclick" : true,
6186         /**
6187          * @event rowclick
6188          * Fires when a row is clicked
6189          * @param {Roo.bootstrap.Table} this
6190          * @param {Roo.Element} el
6191          * @param {Number} rowIndex
6192          * @param {Roo.EventObject} e
6193          */
6194         "rowclick" : true,
6195         /**
6196          * @event rowdblclick
6197          * Fires when a row is double clicked
6198          * @param {Roo.bootstrap.Table} this
6199          * @param {Roo.Element} el
6200          * @param {Number} rowIndex
6201          * @param {Roo.EventObject} e
6202          */
6203         "rowdblclick" : true,
6204         /**
6205          * @event mouseover
6206          * Fires when a mouseover occur
6207          * @param {Roo.bootstrap.Table} this
6208          * @param {Roo.Element} el
6209          * @param {Number} rowIndex
6210          * @param {Number} columnIndex
6211          * @param {Roo.EventObject} e
6212          */
6213         "mouseover" : true,
6214         /**
6215          * @event mouseout
6216          * Fires when a mouseout occur
6217          * @param {Roo.bootstrap.Table} this
6218          * @param {Roo.Element} el
6219          * @param {Number} rowIndex
6220          * @param {Number} columnIndex
6221          * @param {Roo.EventObject} e
6222          */
6223         "mouseout" : true,
6224         /**
6225          * @event rowclass
6226          * Fires when a row is rendered, so you can change add a style to it.
6227          * @param {Roo.bootstrap.Table} this
6228          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6229          */
6230         'rowclass' : true,
6231           /**
6232          * @event rowsrendered
6233          * Fires when all the  rows have been rendered
6234          * @param {Roo.bootstrap.Table} this
6235          */
6236         'rowsrendered' : true,
6237         /**
6238          * @event contextmenu
6239          * The raw contextmenu event for the entire grid.
6240          * @param {Roo.EventObject} e
6241          */
6242         "contextmenu" : true,
6243         /**
6244          * @event rowcontextmenu
6245          * Fires when a row is right clicked
6246          * @param {Roo.bootstrap.Table} this
6247          * @param {Number} rowIndex
6248          * @param {Roo.EventObject} e
6249          */
6250         "rowcontextmenu" : true,
6251         /**
6252          * @event cellcontextmenu
6253          * Fires when a cell is right clicked
6254          * @param {Roo.bootstrap.Table} this
6255          * @param {Number} rowIndex
6256          * @param {Number} cellIndex
6257          * @param {Roo.EventObject} e
6258          */
6259          "cellcontextmenu" : true,
6260          /**
6261          * @event headercontextmenu
6262          * Fires when a header is right clicked
6263          * @param {Roo.bootstrap.Table} this
6264          * @param {Number} columnIndex
6265          * @param {Roo.EventObject} e
6266          */
6267         "headercontextmenu" : true
6268     });
6269 };
6270
6271 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6272     
6273     cls: false,
6274     align: false,
6275     bgcolor: false,
6276     border: false,
6277     cellpadding: false,
6278     cellspacing: false,
6279     frame: false,
6280     rules: false,
6281     sortable: false,
6282     summary: false,
6283     width: false,
6284     striped : false,
6285     scrollBody : false,
6286     bordered: false,
6287     hover:  false,
6288     condensed : false,
6289     responsive : false,
6290     sm : false,
6291     cm : false,
6292     store : false,
6293     loadMask : false,
6294     footerShow : true,
6295     headerShow : true,
6296   
6297     rowSelection : false,
6298     cellSelection : false,
6299     layout : false,
6300     
6301     // Roo.Element - the tbody
6302     mainBody: false,
6303     // Roo.Element - thead element
6304     mainHead: false,
6305     
6306     container: false, // used by gridpanel...
6307     
6308     lazyLoad : false,
6309     
6310     CSS : Roo.util.CSS,
6311     
6312     auto_hide_footer : false,
6313     
6314     getAutoCreate : function()
6315     {
6316         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6317         
6318         cfg = {
6319             tag: 'table',
6320             cls : 'table',
6321             cn : []
6322         };
6323         if (this.scrollBody) {
6324             cfg.cls += ' table-body-fixed';
6325         }    
6326         if (this.striped) {
6327             cfg.cls += ' table-striped';
6328         }
6329         
6330         if (this.hover) {
6331             cfg.cls += ' table-hover';
6332         }
6333         if (this.bordered) {
6334             cfg.cls += ' table-bordered';
6335         }
6336         if (this.condensed) {
6337             cfg.cls += ' table-condensed';
6338         }
6339         if (this.responsive) {
6340             cfg.cls += ' table-responsive';
6341         }
6342         
6343         if (this.cls) {
6344             cfg.cls+=  ' ' +this.cls;
6345         }
6346         
6347         // this lot should be simplifed...
6348         var _t = this;
6349         var cp = [
6350             'align',
6351             'bgcolor',
6352             'border',
6353             'cellpadding',
6354             'cellspacing',
6355             'frame',
6356             'rules',
6357             'sortable',
6358             'summary',
6359             'width'
6360         ].forEach(function(k) {
6361             if (_t[k]) {
6362                 cfg[k] = _t[k];
6363             }
6364         });
6365         
6366         
6367         if (this.layout) {
6368             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6369         }
6370         
6371         if(this.store || this.cm){
6372             if(this.headerShow){
6373                 cfg.cn.push(this.renderHeader());
6374             }
6375             
6376             cfg.cn.push(this.renderBody());
6377             
6378             if(this.footerShow){
6379                 cfg.cn.push(this.renderFooter());
6380             }
6381             // where does this come from?
6382             //cfg.cls+=  ' TableGrid';
6383         }
6384         
6385         return { cn : [ cfg ] };
6386     },
6387     
6388     initEvents : function()
6389     {   
6390         if(!this.store || !this.cm){
6391             return;
6392         }
6393         if (this.selModel) {
6394             this.selModel.initEvents();
6395         }
6396         
6397         
6398         //Roo.log('initEvents with ds!!!!');
6399         
6400         this.mainBody = this.el.select('tbody', true).first();
6401         this.mainHead = this.el.select('thead', true).first();
6402         this.mainFoot = this.el.select('tfoot', true).first();
6403         
6404         
6405         
6406         var _this = this;
6407         
6408         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6409             e.on('click', _this.sort, _this);
6410         });
6411         
6412         this.mainBody.on("click", this.onClick, this);
6413         this.mainBody.on("dblclick", this.onDblClick, this);
6414         
6415         // why is this done????? = it breaks dialogs??
6416         //this.parent().el.setStyle('position', 'relative');
6417         
6418         
6419         if (this.footer) {
6420             this.footer.parentId = this.id;
6421             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6422             
6423             if(this.lazyLoad){
6424                 this.el.select('tfoot tr td').first().addClass('hide');
6425             }
6426         } 
6427         
6428         if(this.loadMask) {
6429             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6430         }
6431         
6432         this.store.on('load', this.onLoad, this);
6433         this.store.on('beforeload', this.onBeforeLoad, this);
6434         this.store.on('update', this.onUpdate, this);
6435         this.store.on('add', this.onAdd, this);
6436         this.store.on("clear", this.clear, this);
6437         
6438         this.el.on("contextmenu", this.onContextMenu, this);
6439         
6440         this.mainBody.on('scroll', this.onBodyScroll, this);
6441         
6442         this.cm.on("headerchange", this.onHeaderChange, this);
6443         
6444         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6445         
6446     },
6447     
6448     onContextMenu : function(e, t)
6449     {
6450         this.processEvent("contextmenu", e);
6451     },
6452     
6453     processEvent : function(name, e)
6454     {
6455         if (name != 'touchstart' ) {
6456             this.fireEvent(name, e);    
6457         }
6458         
6459         var t = e.getTarget();
6460         
6461         var cell = Roo.get(t);
6462         
6463         if(!cell){
6464             return;
6465         }
6466         
6467         if(cell.findParent('tfoot', false, true)){
6468             return;
6469         }
6470         
6471         if(cell.findParent('thead', false, true)){
6472             
6473             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6474                 cell = Roo.get(t).findParent('th', false, true);
6475                 if (!cell) {
6476                     Roo.log("failed to find th in thead?");
6477                     Roo.log(e.getTarget());
6478                     return;
6479                 }
6480             }
6481             
6482             var cellIndex = cell.dom.cellIndex;
6483             
6484             var ename = name == 'touchstart' ? 'click' : name;
6485             this.fireEvent("header" + ename, this, cellIndex, e);
6486             
6487             return;
6488         }
6489         
6490         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6491             cell = Roo.get(t).findParent('td', false, true);
6492             if (!cell) {
6493                 Roo.log("failed to find th in tbody?");
6494                 Roo.log(e.getTarget());
6495                 return;
6496             }
6497         }
6498         
6499         var row = cell.findParent('tr', false, true);
6500         var cellIndex = cell.dom.cellIndex;
6501         var rowIndex = row.dom.rowIndex - 1;
6502         
6503         if(row !== false){
6504             
6505             this.fireEvent("row" + name, this, rowIndex, e);
6506             
6507             if(cell !== false){
6508             
6509                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6510             }
6511         }
6512         
6513     },
6514     
6515     onMouseover : function(e, el)
6516     {
6517         var cell = Roo.get(el);
6518         
6519         if(!cell){
6520             return;
6521         }
6522         
6523         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6524             cell = cell.findParent('td', false, true);
6525         }
6526         
6527         var row = cell.findParent('tr', false, true);
6528         var cellIndex = cell.dom.cellIndex;
6529         var rowIndex = row.dom.rowIndex - 1; // start from 0
6530         
6531         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6532         
6533     },
6534     
6535     onMouseout : function(e, el)
6536     {
6537         var cell = Roo.get(el);
6538         
6539         if(!cell){
6540             return;
6541         }
6542         
6543         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6544             cell = cell.findParent('td', false, true);
6545         }
6546         
6547         var row = cell.findParent('tr', false, true);
6548         var cellIndex = cell.dom.cellIndex;
6549         var rowIndex = row.dom.rowIndex - 1; // start from 0
6550         
6551         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6552         
6553     },
6554     
6555     onClick : function(e, el)
6556     {
6557         var cell = Roo.get(el);
6558         
6559         if(!cell || (!this.cellSelection && !this.rowSelection)){
6560             return;
6561         }
6562         
6563         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6564             cell = cell.findParent('td', false, true);
6565         }
6566         
6567         if(!cell || typeof(cell) == 'undefined'){
6568             return;
6569         }
6570         
6571         var row = cell.findParent('tr', false, true);
6572         
6573         if(!row || typeof(row) == 'undefined'){
6574             return;
6575         }
6576         
6577         var cellIndex = cell.dom.cellIndex;
6578         var rowIndex = this.getRowIndex(row);
6579         
6580         // why??? - should these not be based on SelectionModel?
6581         if(this.cellSelection){
6582             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6583         }
6584         
6585         if(this.rowSelection){
6586             this.fireEvent('rowclick', this, row, rowIndex, e);
6587         }
6588         
6589         
6590     },
6591         
6592     onDblClick : function(e,el)
6593     {
6594         var cell = Roo.get(el);
6595         
6596         if(!cell || (!this.cellSelection && !this.rowSelection)){
6597             return;
6598         }
6599         
6600         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6601             cell = cell.findParent('td', false, true);
6602         }
6603         
6604         if(!cell || typeof(cell) == 'undefined'){
6605             return;
6606         }
6607         
6608         var row = cell.findParent('tr', false, true);
6609         
6610         if(!row || typeof(row) == 'undefined'){
6611             return;
6612         }
6613         
6614         var cellIndex = cell.dom.cellIndex;
6615         var rowIndex = this.getRowIndex(row);
6616         
6617         if(this.cellSelection){
6618             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6619         }
6620         
6621         if(this.rowSelection){
6622             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6623         }
6624     },
6625     
6626     sort : function(e,el)
6627     {
6628         var col = Roo.get(el);
6629         
6630         if(!col.hasClass('sortable')){
6631             return;
6632         }
6633         
6634         var sort = col.attr('sort');
6635         var dir = 'ASC';
6636         
6637         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6638             dir = 'DESC';
6639         }
6640         
6641         this.store.sortInfo = {field : sort, direction : dir};
6642         
6643         if (this.footer) {
6644             Roo.log("calling footer first");
6645             this.footer.onClick('first');
6646         } else {
6647         
6648             this.store.load({ params : { start : 0 } });
6649         }
6650     },
6651     
6652     renderHeader : function()
6653     {
6654         var header = {
6655             tag: 'thead',
6656             cn : []
6657         };
6658         
6659         var cm = this.cm;
6660         this.totalWidth = 0;
6661         
6662         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6663             
6664             var config = cm.config[i];
6665             
6666             var c = {
6667                 tag: 'th',
6668                 cls : 'x-hcol-' + i,
6669                 style : '',
6670                 html: cm.getColumnHeader(i)
6671             };
6672             
6673             var hh = '';
6674             
6675             if(typeof(config.sortable) != 'undefined' && config.sortable){
6676                 c.cls = 'sortable';
6677                 c.html = '<i class="glyphicon"></i>' + c.html;
6678             }
6679             
6680             if(typeof(config.lgHeader) != 'undefined'){
6681                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6682             }
6683             
6684             if(typeof(config.mdHeader) != 'undefined'){
6685                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6686             }
6687             
6688             if(typeof(config.smHeader) != 'undefined'){
6689                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6690             }
6691             
6692             if(typeof(config.xsHeader) != 'undefined'){
6693                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6694             }
6695             
6696             if(hh.length){
6697                 c.html = hh;
6698             }
6699             
6700             if(typeof(config.tooltip) != 'undefined'){
6701                 c.tooltip = config.tooltip;
6702             }
6703             
6704             if(typeof(config.colspan) != 'undefined'){
6705                 c.colspan = config.colspan;
6706             }
6707             
6708             if(typeof(config.hidden) != 'undefined' && config.hidden){
6709                 c.style += ' display:none;';
6710             }
6711             
6712             if(typeof(config.dataIndex) != 'undefined'){
6713                 c.sort = config.dataIndex;
6714             }
6715             
6716            
6717             
6718             if(typeof(config.align) != 'undefined' && config.align.length){
6719                 c.style += ' text-align:' + config.align + ';';
6720             }
6721             
6722             if(typeof(config.width) != 'undefined'){
6723                 c.style += ' width:' + config.width + 'px;';
6724                 this.totalWidth += config.width;
6725             } else {
6726                 this.totalWidth += 100; // assume minimum of 100 per column?
6727             }
6728             
6729             if(typeof(config.cls) != 'undefined'){
6730                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6731             }
6732             
6733             ['xs','sm','md','lg'].map(function(size){
6734                 
6735                 if(typeof(config[size]) == 'undefined'){
6736                     return;
6737                 }
6738                 
6739                 if (!config[size]) { // 0 = hidden
6740                     c.cls += ' hidden-' + size;
6741                     return;
6742                 }
6743                 
6744                 c.cls += ' col-' + size + '-' + config[size];
6745
6746             });
6747             
6748             header.cn.push(c)
6749         }
6750         
6751         return header;
6752     },
6753     
6754     renderBody : function()
6755     {
6756         var body = {
6757             tag: 'tbody',
6758             cn : [
6759                 {
6760                     tag: 'tr',
6761                     cn : [
6762                         {
6763                             tag : 'td',
6764                             colspan :  this.cm.getColumnCount()
6765                         }
6766                     ]
6767                 }
6768             ]
6769         };
6770         
6771         return body;
6772     },
6773     
6774     renderFooter : function()
6775     {
6776         var footer = {
6777             tag: 'tfoot',
6778             cn : [
6779                 {
6780                     tag: 'tr',
6781                     cn : [
6782                         {
6783                             tag : 'td',
6784                             colspan :  this.cm.getColumnCount()
6785                         }
6786                     ]
6787                 }
6788             ]
6789         };
6790         
6791         return footer;
6792     },
6793     
6794     
6795     
6796     onLoad : function()
6797     {
6798 //        Roo.log('ds onload');
6799         this.clear();
6800         
6801         var _this = this;
6802         var cm = this.cm;
6803         var ds = this.store;
6804         
6805         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6806             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6807             if (_this.store.sortInfo) {
6808                     
6809                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6810                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6811                 }
6812                 
6813                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6814                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6815                 }
6816             }
6817         });
6818         
6819         var tbody =  this.mainBody;
6820               
6821         if(ds.getCount() > 0){
6822             ds.data.each(function(d,rowIndex){
6823                 var row =  this.renderRow(cm, ds, rowIndex);
6824                 
6825                 tbody.createChild(row);
6826                 
6827                 var _this = this;
6828                 
6829                 if(row.cellObjects.length){
6830                     Roo.each(row.cellObjects, function(r){
6831                         _this.renderCellObject(r);
6832                     })
6833                 }
6834                 
6835             }, this);
6836         }
6837         
6838         var tfoot = this.el.select('tfoot', true).first();
6839         
6840         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6841             
6842             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6843             
6844             var total = this.ds.getTotalCount();
6845             
6846             if(this.footer.pageSize < total){
6847                 this.mainFoot.show();
6848             }
6849         }
6850         
6851         Roo.each(this.el.select('tbody td', true).elements, function(e){
6852             e.on('mouseover', _this.onMouseover, _this);
6853         });
6854         
6855         Roo.each(this.el.select('tbody td', true).elements, function(e){
6856             e.on('mouseout', _this.onMouseout, _this);
6857         });
6858         this.fireEvent('rowsrendered', this);
6859         
6860         this.autoSize();
6861     },
6862     
6863     
6864     onUpdate : function(ds,record)
6865     {
6866         this.refreshRow(record);
6867         this.autoSize();
6868     },
6869     
6870     onRemove : function(ds, record, index, isUpdate){
6871         if(isUpdate !== true){
6872             this.fireEvent("beforerowremoved", this, index, record);
6873         }
6874         var bt = this.mainBody.dom;
6875         
6876         var rows = this.el.select('tbody > tr', true).elements;
6877         
6878         if(typeof(rows[index]) != 'undefined'){
6879             bt.removeChild(rows[index].dom);
6880         }
6881         
6882 //        if(bt.rows[index]){
6883 //            bt.removeChild(bt.rows[index]);
6884 //        }
6885         
6886         if(isUpdate !== true){
6887             //this.stripeRows(index);
6888             //this.syncRowHeights(index, index);
6889             //this.layout();
6890             this.fireEvent("rowremoved", this, index, record);
6891         }
6892     },
6893     
6894     onAdd : function(ds, records, rowIndex)
6895     {
6896         //Roo.log('on Add called');
6897         // - note this does not handle multiple adding very well..
6898         var bt = this.mainBody.dom;
6899         for (var i =0 ; i < records.length;i++) {
6900             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6901             //Roo.log(records[i]);
6902             //Roo.log(this.store.getAt(rowIndex+i));
6903             this.insertRow(this.store, rowIndex + i, false);
6904             return;
6905         }
6906         
6907     },
6908     
6909     
6910     refreshRow : function(record){
6911         var ds = this.store, index;
6912         if(typeof record == 'number'){
6913             index = record;
6914             record = ds.getAt(index);
6915         }else{
6916             index = ds.indexOf(record);
6917         }
6918         this.insertRow(ds, index, true);
6919         this.autoSize();
6920         this.onRemove(ds, record, index+1, true);
6921         this.autoSize();
6922         //this.syncRowHeights(index, index);
6923         //this.layout();
6924         this.fireEvent("rowupdated", this, index, record);
6925     },
6926     
6927     insertRow : function(dm, rowIndex, isUpdate){
6928         
6929         if(!isUpdate){
6930             this.fireEvent("beforerowsinserted", this, rowIndex);
6931         }
6932             //var s = this.getScrollState();
6933         var row = this.renderRow(this.cm, this.store, rowIndex);
6934         // insert before rowIndex..
6935         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6936         
6937         var _this = this;
6938                 
6939         if(row.cellObjects.length){
6940             Roo.each(row.cellObjects, function(r){
6941                 _this.renderCellObject(r);
6942             })
6943         }
6944             
6945         if(!isUpdate){
6946             this.fireEvent("rowsinserted", this, rowIndex);
6947             //this.syncRowHeights(firstRow, lastRow);
6948             //this.stripeRows(firstRow);
6949             //this.layout();
6950         }
6951         
6952     },
6953     
6954     
6955     getRowDom : function(rowIndex)
6956     {
6957         var rows = this.el.select('tbody > tr', true).elements;
6958         
6959         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6960         
6961     },
6962     // returns the object tree for a tr..
6963   
6964     
6965     renderRow : function(cm, ds, rowIndex) 
6966     {
6967         var d = ds.getAt(rowIndex);
6968         
6969         var row = {
6970             tag : 'tr',
6971             cls : 'x-row-' + rowIndex,
6972             cn : []
6973         };
6974             
6975         var cellObjects = [];
6976         
6977         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6978             var config = cm.config[i];
6979             
6980             var renderer = cm.getRenderer(i);
6981             var value = '';
6982             var id = false;
6983             
6984             if(typeof(renderer) !== 'undefined'){
6985                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6986             }
6987             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6988             // and are rendered into the cells after the row is rendered - using the id for the element.
6989             
6990             if(typeof(value) === 'object'){
6991                 id = Roo.id();
6992                 cellObjects.push({
6993                     container : id,
6994                     cfg : value 
6995                 })
6996             }
6997             
6998             var rowcfg = {
6999                 record: d,
7000                 rowIndex : rowIndex,
7001                 colIndex : i,
7002                 rowClass : ''
7003             };
7004
7005             this.fireEvent('rowclass', this, rowcfg);
7006             
7007             var td = {
7008                 tag: 'td',
7009                 cls : rowcfg.rowClass + ' x-col-' + i,
7010                 style: '',
7011                 html: (typeof(value) === 'object') ? '' : value
7012             };
7013             
7014             if (id) {
7015                 td.id = id;
7016             }
7017             
7018             if(typeof(config.colspan) != 'undefined'){
7019                 td.colspan = config.colspan;
7020             }
7021             
7022             if(typeof(config.hidden) != 'undefined' && config.hidden){
7023                 td.style += ' display:none;';
7024             }
7025             
7026             if(typeof(config.align) != 'undefined' && config.align.length){
7027                 td.style += ' text-align:' + config.align + ';';
7028             }
7029             if(typeof(config.valign) != 'undefined' && config.valign.length){
7030                 td.style += ' vertical-align:' + config.valign + ';';
7031             }
7032             
7033             if(typeof(config.width) != 'undefined'){
7034                 td.style += ' width:' +  config.width + 'px;';
7035             }
7036             
7037             if(typeof(config.cursor) != 'undefined'){
7038                 td.style += ' cursor:' +  config.cursor + ';';
7039             }
7040             
7041             if(typeof(config.cls) != 'undefined'){
7042                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7043             }
7044             
7045             ['xs','sm','md','lg'].map(function(size){
7046                 
7047                 if(typeof(config[size]) == 'undefined'){
7048                     return;
7049                 }
7050                 
7051                 if (!config[size]) { // 0 = hidden
7052                     td.cls += ' hidden-' + size;
7053                     return;
7054                 }
7055                 
7056                 td.cls += ' col-' + size + '-' + config[size];
7057
7058             });
7059             
7060             row.cn.push(td);
7061            
7062         }
7063         
7064         row.cellObjects = cellObjects;
7065         
7066         return row;
7067           
7068     },
7069     
7070     
7071     
7072     onBeforeLoad : function()
7073     {
7074         
7075     },
7076      /**
7077      * Remove all rows
7078      */
7079     clear : function()
7080     {
7081         this.el.select('tbody', true).first().dom.innerHTML = '';
7082     },
7083     /**
7084      * Show or hide a row.
7085      * @param {Number} rowIndex to show or hide
7086      * @param {Boolean} state hide
7087      */
7088     setRowVisibility : function(rowIndex, state)
7089     {
7090         var bt = this.mainBody.dom;
7091         
7092         var rows = this.el.select('tbody > tr', true).elements;
7093         
7094         if(typeof(rows[rowIndex]) == 'undefined'){
7095             return;
7096         }
7097         rows[rowIndex].dom.style.display = state ? '' : 'none';
7098     },
7099     
7100     
7101     getSelectionModel : function(){
7102         if(!this.selModel){
7103             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7104         }
7105         return this.selModel;
7106     },
7107     /*
7108      * Render the Roo.bootstrap object from renderder
7109      */
7110     renderCellObject : function(r)
7111     {
7112         var _this = this;
7113         
7114         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7115         
7116         var t = r.cfg.render(r.container);
7117         
7118         if(r.cfg.cn){
7119             Roo.each(r.cfg.cn, function(c){
7120                 var child = {
7121                     container: t.getChildContainer(),
7122                     cfg: c
7123                 };
7124                 _this.renderCellObject(child);
7125             })
7126         }
7127     },
7128     
7129     getRowIndex : function(row)
7130     {
7131         var rowIndex = -1;
7132         
7133         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7134             if(el != row){
7135                 return;
7136             }
7137             
7138             rowIndex = index;
7139         });
7140         
7141         return rowIndex;
7142     },
7143      /**
7144      * Returns the grid's underlying element = used by panel.Grid
7145      * @return {Element} The element
7146      */
7147     getGridEl : function(){
7148         return this.el;
7149     },
7150      /**
7151      * Forces a resize - used by panel.Grid
7152      * @return {Element} The element
7153      */
7154     autoSize : function()
7155     {
7156         //var ctr = Roo.get(this.container.dom.parentElement);
7157         var ctr = Roo.get(this.el.dom);
7158         
7159         var thd = this.getGridEl().select('thead',true).first();
7160         var tbd = this.getGridEl().select('tbody', true).first();
7161         var tfd = this.getGridEl().select('tfoot', true).first();
7162         
7163         var cw = ctr.getWidth();
7164         
7165         if (tbd) {
7166             
7167             tbd.setSize(ctr.getWidth(),
7168                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7169             );
7170             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7171             cw -= barsize;
7172         }
7173         cw = Math.max(cw, this.totalWidth);
7174         this.getGridEl().select('tr',true).setWidth(cw);
7175         // resize 'expandable coloumn?
7176         
7177         return; // we doe not have a view in this design..
7178         
7179     },
7180     onBodyScroll: function()
7181     {
7182         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7183         if(this.mainHead){
7184             this.mainHead.setStyle({
7185                 'position' : 'relative',
7186                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7187             });
7188         }
7189         
7190         if(this.lazyLoad){
7191             
7192             var scrollHeight = this.mainBody.dom.scrollHeight;
7193             
7194             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7195             
7196             var height = this.mainBody.getHeight();
7197             
7198             if(scrollHeight - height == scrollTop) {
7199                 
7200                 var total = this.ds.getTotalCount();
7201                 
7202                 if(this.footer.cursor + this.footer.pageSize < total){
7203                     
7204                     this.footer.ds.load({
7205                         params : {
7206                             start : this.footer.cursor + this.footer.pageSize,
7207                             limit : this.footer.pageSize
7208                         },
7209                         add : true
7210                     });
7211                 }
7212             }
7213             
7214         }
7215     },
7216     
7217     onHeaderChange : function()
7218     {
7219         var header = this.renderHeader();
7220         var table = this.el.select('table', true).first();
7221         
7222         this.mainHead.remove();
7223         this.mainHead = table.createChild(header, this.mainBody, false);
7224     },
7225     
7226     onHiddenChange : function(colModel, colIndex, hidden)
7227     {
7228         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7229         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7230         
7231         this.CSS.updateRule(thSelector, "display", "");
7232         this.CSS.updateRule(tdSelector, "display", "");
7233         
7234         if(hidden){
7235             this.CSS.updateRule(thSelector, "display", "none");
7236             this.CSS.updateRule(tdSelector, "display", "none");
7237         }
7238         
7239         this.onHeaderChange();
7240         this.onLoad();
7241     },
7242     
7243     setColumnWidth: function(col_index, width)
7244     {
7245         // width = "md-2 xs-2..."
7246         if(!this.colModel.config[col_index]) {
7247             return;
7248         }
7249         
7250         var w = width.split(" ");
7251         
7252         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7253         
7254         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7255         
7256         
7257         for(var j = 0; j < w.length; j++) {
7258             
7259             if(!w[j]) {
7260                 continue;
7261             }
7262             
7263             var size_cls = w[j].split("-");
7264             
7265             if(!Number.isInteger(size_cls[1] * 1)) {
7266                 continue;
7267             }
7268             
7269             if(!this.colModel.config[col_index][size_cls[0]]) {
7270                 continue;
7271             }
7272             
7273             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7274                 continue;
7275             }
7276             
7277             h_row[0].classList.replace(
7278                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7279                 "col-"+size_cls[0]+"-"+size_cls[1]
7280             );
7281             
7282             for(var i = 0; i < rows.length; i++) {
7283                 
7284                 var size_cls = w[j].split("-");
7285                 
7286                 if(!Number.isInteger(size_cls[1] * 1)) {
7287                     continue;
7288                 }
7289                 
7290                 if(!this.colModel.config[col_index][size_cls[0]]) {
7291                     continue;
7292                 }
7293                 
7294                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7295                     continue;
7296                 }
7297                 
7298                 rows[i].classList.replace(
7299                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7300                     "col-"+size_cls[0]+"-"+size_cls[1]
7301                 );
7302             }
7303             
7304             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7305         }
7306     }
7307 });
7308
7309  
7310
7311  /*
7312  * - LGPL
7313  *
7314  * table cell
7315  * 
7316  */
7317
7318 /**
7319  * @class Roo.bootstrap.TableCell
7320  * @extends Roo.bootstrap.Component
7321  * Bootstrap TableCell class
7322  * @cfg {String} html cell contain text
7323  * @cfg {String} cls cell class
7324  * @cfg {String} tag cell tag (td|th) default td
7325  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7326  * @cfg {String} align Aligns the content in a cell
7327  * @cfg {String} axis Categorizes cells
7328  * @cfg {String} bgcolor Specifies the background color of a cell
7329  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7330  * @cfg {Number} colspan Specifies the number of columns a cell should span
7331  * @cfg {String} headers Specifies one or more header cells a cell is related to
7332  * @cfg {Number} height Sets the height of a cell
7333  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7334  * @cfg {Number} rowspan Sets the number of rows a cell should span
7335  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7336  * @cfg {String} valign Vertical aligns the content in a cell
7337  * @cfg {Number} width Specifies the width of a cell
7338  * 
7339  * @constructor
7340  * Create a new TableCell
7341  * @param {Object} config The config object
7342  */
7343
7344 Roo.bootstrap.TableCell = function(config){
7345     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7346 };
7347
7348 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7349     
7350     html: false,
7351     cls: false,
7352     tag: false,
7353     abbr: false,
7354     align: false,
7355     axis: false,
7356     bgcolor: false,
7357     charoff: false,
7358     colspan: false,
7359     headers: false,
7360     height: false,
7361     nowrap: false,
7362     rowspan: false,
7363     scope: false,
7364     valign: false,
7365     width: false,
7366     
7367     
7368     getAutoCreate : function(){
7369         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7370         
7371         cfg = {
7372             tag: 'td'
7373         };
7374         
7375         if(this.tag){
7376             cfg.tag = this.tag;
7377         }
7378         
7379         if (this.html) {
7380             cfg.html=this.html
7381         }
7382         if (this.cls) {
7383             cfg.cls=this.cls
7384         }
7385         if (this.abbr) {
7386             cfg.abbr=this.abbr
7387         }
7388         if (this.align) {
7389             cfg.align=this.align
7390         }
7391         if (this.axis) {
7392             cfg.axis=this.axis
7393         }
7394         if (this.bgcolor) {
7395             cfg.bgcolor=this.bgcolor
7396         }
7397         if (this.charoff) {
7398             cfg.charoff=this.charoff
7399         }
7400         if (this.colspan) {
7401             cfg.colspan=this.colspan
7402         }
7403         if (this.headers) {
7404             cfg.headers=this.headers
7405         }
7406         if (this.height) {
7407             cfg.height=this.height
7408         }
7409         if (this.nowrap) {
7410             cfg.nowrap=this.nowrap
7411         }
7412         if (this.rowspan) {
7413             cfg.rowspan=this.rowspan
7414         }
7415         if (this.scope) {
7416             cfg.scope=this.scope
7417         }
7418         if (this.valign) {
7419             cfg.valign=this.valign
7420         }
7421         if (this.width) {
7422             cfg.width=this.width
7423         }
7424         
7425         
7426         return cfg;
7427     }
7428    
7429 });
7430
7431  
7432
7433  /*
7434  * - LGPL
7435  *
7436  * table row
7437  * 
7438  */
7439
7440 /**
7441  * @class Roo.bootstrap.TableRow
7442  * @extends Roo.bootstrap.Component
7443  * Bootstrap TableRow class
7444  * @cfg {String} cls row class
7445  * @cfg {String} align Aligns the content in a table row
7446  * @cfg {String} bgcolor Specifies a background color for a table row
7447  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7448  * @cfg {String} valign Vertical aligns the content in a table row
7449  * 
7450  * @constructor
7451  * Create a new TableRow
7452  * @param {Object} config The config object
7453  */
7454
7455 Roo.bootstrap.TableRow = function(config){
7456     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7457 };
7458
7459 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7460     
7461     cls: false,
7462     align: false,
7463     bgcolor: false,
7464     charoff: false,
7465     valign: false,
7466     
7467     getAutoCreate : function(){
7468         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7469         
7470         cfg = {
7471             tag: 'tr'
7472         };
7473             
7474         if(this.cls){
7475             cfg.cls = this.cls;
7476         }
7477         if(this.align){
7478             cfg.align = this.align;
7479         }
7480         if(this.bgcolor){
7481             cfg.bgcolor = this.bgcolor;
7482         }
7483         if(this.charoff){
7484             cfg.charoff = this.charoff;
7485         }
7486         if(this.valign){
7487             cfg.valign = this.valign;
7488         }
7489         
7490         return cfg;
7491     }
7492    
7493 });
7494
7495  
7496
7497  /*
7498  * - LGPL
7499  *
7500  * table body
7501  * 
7502  */
7503
7504 /**
7505  * @class Roo.bootstrap.TableBody
7506  * @extends Roo.bootstrap.Component
7507  * Bootstrap TableBody class
7508  * @cfg {String} cls element class
7509  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7510  * @cfg {String} align Aligns the content inside the element
7511  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7512  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7513  * 
7514  * @constructor
7515  * Create a new TableBody
7516  * @param {Object} config The config object
7517  */
7518
7519 Roo.bootstrap.TableBody = function(config){
7520     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7521 };
7522
7523 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7524     
7525     cls: false,
7526     tag: false,
7527     align: false,
7528     charoff: false,
7529     valign: false,
7530     
7531     getAutoCreate : function(){
7532         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7533         
7534         cfg = {
7535             tag: 'tbody'
7536         };
7537             
7538         if (this.cls) {
7539             cfg.cls=this.cls
7540         }
7541         if(this.tag){
7542             cfg.tag = this.tag;
7543         }
7544         
7545         if(this.align){
7546             cfg.align = this.align;
7547         }
7548         if(this.charoff){
7549             cfg.charoff = this.charoff;
7550         }
7551         if(this.valign){
7552             cfg.valign = this.valign;
7553         }
7554         
7555         return cfg;
7556     }
7557     
7558     
7559 //    initEvents : function()
7560 //    {
7561 //        
7562 //        if(!this.store){
7563 //            return;
7564 //        }
7565 //        
7566 //        this.store = Roo.factory(this.store, Roo.data);
7567 //        this.store.on('load', this.onLoad, this);
7568 //        
7569 //        this.store.load();
7570 //        
7571 //    },
7572 //    
7573 //    onLoad: function () 
7574 //    {   
7575 //        this.fireEvent('load', this);
7576 //    }
7577 //    
7578 //   
7579 });
7580
7581  
7582
7583  /*
7584  * Based on:
7585  * Ext JS Library 1.1.1
7586  * Copyright(c) 2006-2007, Ext JS, LLC.
7587  *
7588  * Originally Released Under LGPL - original licence link has changed is not relivant.
7589  *
7590  * Fork - LGPL
7591  * <script type="text/javascript">
7592  */
7593
7594 // as we use this in bootstrap.
7595 Roo.namespace('Roo.form');
7596  /**
7597  * @class Roo.form.Action
7598  * Internal Class used to handle form actions
7599  * @constructor
7600  * @param {Roo.form.BasicForm} el The form element or its id
7601  * @param {Object} config Configuration options
7602  */
7603
7604  
7605  
7606 // define the action interface
7607 Roo.form.Action = function(form, options){
7608     this.form = form;
7609     this.options = options || {};
7610 };
7611 /**
7612  * Client Validation Failed
7613  * @const 
7614  */
7615 Roo.form.Action.CLIENT_INVALID = 'client';
7616 /**
7617  * Server Validation Failed
7618  * @const 
7619  */
7620 Roo.form.Action.SERVER_INVALID = 'server';
7621  /**
7622  * Connect to Server Failed
7623  * @const 
7624  */
7625 Roo.form.Action.CONNECT_FAILURE = 'connect';
7626 /**
7627  * Reading Data from Server Failed
7628  * @const 
7629  */
7630 Roo.form.Action.LOAD_FAILURE = 'load';
7631
7632 Roo.form.Action.prototype = {
7633     type : 'default',
7634     failureType : undefined,
7635     response : undefined,
7636     result : undefined,
7637
7638     // interface method
7639     run : function(options){
7640
7641     },
7642
7643     // interface method
7644     success : function(response){
7645
7646     },
7647
7648     // interface method
7649     handleResponse : function(response){
7650
7651     },
7652
7653     // default connection failure
7654     failure : function(response){
7655         
7656         this.response = response;
7657         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7658         this.form.afterAction(this, false);
7659     },
7660
7661     processResponse : function(response){
7662         this.response = response;
7663         if(!response.responseText){
7664             return true;
7665         }
7666         this.result = this.handleResponse(response);
7667         return this.result;
7668     },
7669
7670     // utility functions used internally
7671     getUrl : function(appendParams){
7672         var url = this.options.url || this.form.url || this.form.el.dom.action;
7673         if(appendParams){
7674             var p = this.getParams();
7675             if(p){
7676                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7677             }
7678         }
7679         return url;
7680     },
7681
7682     getMethod : function(){
7683         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7684     },
7685
7686     getParams : function(){
7687         var bp = this.form.baseParams;
7688         var p = this.options.params;
7689         if(p){
7690             if(typeof p == "object"){
7691                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7692             }else if(typeof p == 'string' && bp){
7693                 p += '&' + Roo.urlEncode(bp);
7694             }
7695         }else if(bp){
7696             p = Roo.urlEncode(bp);
7697         }
7698         return p;
7699     },
7700
7701     createCallback : function(){
7702         return {
7703             success: this.success,
7704             failure: this.failure,
7705             scope: this,
7706             timeout: (this.form.timeout*1000),
7707             upload: this.form.fileUpload ? this.success : undefined
7708         };
7709     }
7710 };
7711
7712 Roo.form.Action.Submit = function(form, options){
7713     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7714 };
7715
7716 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7717     type : 'submit',
7718
7719     haveProgress : false,
7720     uploadComplete : false,
7721     
7722     // uploadProgress indicator.
7723     uploadProgress : function()
7724     {
7725         if (!this.form.progressUrl) {
7726             return;
7727         }
7728         
7729         if (!this.haveProgress) {
7730             Roo.MessageBox.progress("Uploading", "Uploading");
7731         }
7732         if (this.uploadComplete) {
7733            Roo.MessageBox.hide();
7734            return;
7735         }
7736         
7737         this.haveProgress = true;
7738    
7739         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7740         
7741         var c = new Roo.data.Connection();
7742         c.request({
7743             url : this.form.progressUrl,
7744             params: {
7745                 id : uid
7746             },
7747             method: 'GET',
7748             success : function(req){
7749                //console.log(data);
7750                 var rdata = false;
7751                 var edata;
7752                 try  {
7753                    rdata = Roo.decode(req.responseText)
7754                 } catch (e) {
7755                     Roo.log("Invalid data from server..");
7756                     Roo.log(edata);
7757                     return;
7758                 }
7759                 if (!rdata || !rdata.success) {
7760                     Roo.log(rdata);
7761                     Roo.MessageBox.alert(Roo.encode(rdata));
7762                     return;
7763                 }
7764                 var data = rdata.data;
7765                 
7766                 if (this.uploadComplete) {
7767                    Roo.MessageBox.hide();
7768                    return;
7769                 }
7770                    
7771                 if (data){
7772                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7773                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7774                     );
7775                 }
7776                 this.uploadProgress.defer(2000,this);
7777             },
7778        
7779             failure: function(data) {
7780                 Roo.log('progress url failed ');
7781                 Roo.log(data);
7782             },
7783             scope : this
7784         });
7785            
7786     },
7787     
7788     
7789     run : function()
7790     {
7791         // run get Values on the form, so it syncs any secondary forms.
7792         this.form.getValues();
7793         
7794         var o = this.options;
7795         var method = this.getMethod();
7796         var isPost = method == 'POST';
7797         if(o.clientValidation === false || this.form.isValid()){
7798             
7799             if (this.form.progressUrl) {
7800                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7801                     (new Date() * 1) + '' + Math.random());
7802                     
7803             } 
7804             
7805             
7806             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7807                 form:this.form.el.dom,
7808                 url:this.getUrl(!isPost),
7809                 method: method,
7810                 params:isPost ? this.getParams() : null,
7811                 isUpload: this.form.fileUpload
7812             }));
7813             
7814             this.uploadProgress();
7815
7816         }else if (o.clientValidation !== false){ // client validation failed
7817             this.failureType = Roo.form.Action.CLIENT_INVALID;
7818             this.form.afterAction(this, false);
7819         }
7820     },
7821
7822     success : function(response)
7823     {
7824         this.uploadComplete= true;
7825         if (this.haveProgress) {
7826             Roo.MessageBox.hide();
7827         }
7828         
7829         
7830         var result = this.processResponse(response);
7831         if(result === true || result.success){
7832             this.form.afterAction(this, true);
7833             return;
7834         }
7835         if(result.errors){
7836             this.form.markInvalid(result.errors);
7837             this.failureType = Roo.form.Action.SERVER_INVALID;
7838         }
7839         this.form.afterAction(this, false);
7840     },
7841     failure : function(response)
7842     {
7843         this.uploadComplete= true;
7844         if (this.haveProgress) {
7845             Roo.MessageBox.hide();
7846         }
7847         
7848         this.response = response;
7849         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7850         this.form.afterAction(this, false);
7851     },
7852     
7853     handleResponse : function(response){
7854         if(this.form.errorReader){
7855             var rs = this.form.errorReader.read(response);
7856             var errors = [];
7857             if(rs.records){
7858                 for(var i = 0, len = rs.records.length; i < len; i++) {
7859                     var r = rs.records[i];
7860                     errors[i] = r.data;
7861                 }
7862             }
7863             if(errors.length < 1){
7864                 errors = null;
7865             }
7866             return {
7867                 success : rs.success,
7868                 errors : errors
7869             };
7870         }
7871         var ret = false;
7872         try {
7873             ret = Roo.decode(response.responseText);
7874         } catch (e) {
7875             ret = {
7876                 success: false,
7877                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7878                 errors : []
7879             };
7880         }
7881         return ret;
7882         
7883     }
7884 });
7885
7886
7887 Roo.form.Action.Load = function(form, options){
7888     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7889     this.reader = this.form.reader;
7890 };
7891
7892 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7893     type : 'load',
7894
7895     run : function(){
7896         
7897         Roo.Ajax.request(Roo.apply(
7898                 this.createCallback(), {
7899                     method:this.getMethod(),
7900                     url:this.getUrl(false),
7901                     params:this.getParams()
7902         }));
7903     },
7904
7905     success : function(response){
7906         
7907         var result = this.processResponse(response);
7908         if(result === true || !result.success || !result.data){
7909             this.failureType = Roo.form.Action.LOAD_FAILURE;
7910             this.form.afterAction(this, false);
7911             return;
7912         }
7913         this.form.clearInvalid();
7914         this.form.setValues(result.data);
7915         this.form.afterAction(this, true);
7916     },
7917
7918     handleResponse : function(response){
7919         if(this.form.reader){
7920             var rs = this.form.reader.read(response);
7921             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7922             return {
7923                 success : rs.success,
7924                 data : data
7925             };
7926         }
7927         return Roo.decode(response.responseText);
7928     }
7929 });
7930
7931 Roo.form.Action.ACTION_TYPES = {
7932     'load' : Roo.form.Action.Load,
7933     'submit' : Roo.form.Action.Submit
7934 };/*
7935  * - LGPL
7936  *
7937  * form
7938  *
7939  */
7940
7941 /**
7942  * @class Roo.bootstrap.Form
7943  * @extends Roo.bootstrap.Component
7944  * Bootstrap Form class
7945  * @cfg {String} method  GET | POST (default POST)
7946  * @cfg {String} labelAlign top | left (default top)
7947  * @cfg {String} align left  | right - for navbars
7948  * @cfg {Boolean} loadMask load mask when submit (default true)
7949
7950  *
7951  * @constructor
7952  * Create a new Form
7953  * @param {Object} config The config object
7954  */
7955
7956
7957 Roo.bootstrap.Form = function(config){
7958     
7959     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7960     
7961     Roo.bootstrap.Form.popover.apply();
7962     
7963     this.addEvents({
7964         /**
7965          * @event clientvalidation
7966          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7967          * @param {Form} this
7968          * @param {Boolean} valid true if the form has passed client-side validation
7969          */
7970         clientvalidation: true,
7971         /**
7972          * @event beforeaction
7973          * Fires before any action is performed. Return false to cancel the action.
7974          * @param {Form} this
7975          * @param {Action} action The action to be performed
7976          */
7977         beforeaction: true,
7978         /**
7979          * @event actionfailed
7980          * Fires when an action fails.
7981          * @param {Form} this
7982          * @param {Action} action The action that failed
7983          */
7984         actionfailed : true,
7985         /**
7986          * @event actioncomplete
7987          * Fires when an action is completed.
7988          * @param {Form} this
7989          * @param {Action} action The action that completed
7990          */
7991         actioncomplete : true
7992     });
7993 };
7994
7995 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7996
7997      /**
7998      * @cfg {String} method
7999      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8000      */
8001     method : 'POST',
8002     /**
8003      * @cfg {String} url
8004      * The URL to use for form actions if one isn't supplied in the action options.
8005      */
8006     /**
8007      * @cfg {Boolean} fileUpload
8008      * Set to true if this form is a file upload.
8009      */
8010
8011     /**
8012      * @cfg {Object} baseParams
8013      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8014      */
8015
8016     /**
8017      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8018      */
8019     timeout: 30,
8020     /**
8021      * @cfg {Sting} align (left|right) for navbar forms
8022      */
8023     align : 'left',
8024
8025     // private
8026     activeAction : null,
8027
8028     /**
8029      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8030      * element by passing it or its id or mask the form itself by passing in true.
8031      * @type Mixed
8032      */
8033     waitMsgTarget : false,
8034
8035     loadMask : true,
8036     
8037     /**
8038      * @cfg {Boolean} errorMask (true|false) default false
8039      */
8040     errorMask : false,
8041     
8042     /**
8043      * @cfg {Number} maskOffset Default 100
8044      */
8045     maskOffset : 100,
8046     
8047     /**
8048      * @cfg {Boolean} maskBody
8049      */
8050     maskBody : false,
8051
8052     getAutoCreate : function(){
8053
8054         var cfg = {
8055             tag: 'form',
8056             method : this.method || 'POST',
8057             id : this.id || Roo.id(),
8058             cls : ''
8059         };
8060         if (this.parent().xtype.match(/^Nav/)) {
8061             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8062
8063         }
8064
8065         if (this.labelAlign == 'left' ) {
8066             cfg.cls += ' form-horizontal';
8067         }
8068
8069
8070         return cfg;
8071     },
8072     initEvents : function()
8073     {
8074         this.el.on('submit', this.onSubmit, this);
8075         // this was added as random key presses on the form where triggering form submit.
8076         this.el.on('keypress', function(e) {
8077             if (e.getCharCode() != 13) {
8078                 return true;
8079             }
8080             // we might need to allow it for textareas.. and some other items.
8081             // check e.getTarget().
8082
8083             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8084                 return true;
8085             }
8086
8087             Roo.log("keypress blocked");
8088
8089             e.preventDefault();
8090             return false;
8091         });
8092         
8093     },
8094     // private
8095     onSubmit : function(e){
8096         e.stopEvent();
8097     },
8098
8099      /**
8100      * Returns true if client-side validation on the form is successful.
8101      * @return Boolean
8102      */
8103     isValid : function(){
8104         var items = this.getItems();
8105         var valid = true;
8106         var target = false;
8107         
8108         items.each(function(f){
8109             
8110             if(f.validate()){
8111                 return;
8112             }
8113             
8114             Roo.log('invalid field: ' + f.name);
8115             
8116             valid = false;
8117
8118             if(!target && f.el.isVisible(true)){
8119                 target = f;
8120             }
8121            
8122         });
8123         
8124         if(this.errorMask && !valid){
8125             Roo.bootstrap.Form.popover.mask(this, target);
8126         }
8127         
8128         return valid;
8129     },
8130     
8131     /**
8132      * Returns true if any fields in this form have changed since their original load.
8133      * @return Boolean
8134      */
8135     isDirty : function(){
8136         var dirty = false;
8137         var items = this.getItems();
8138         items.each(function(f){
8139            if(f.isDirty()){
8140                dirty = true;
8141                return false;
8142            }
8143            return true;
8144         });
8145         return dirty;
8146     },
8147      /**
8148      * Performs a predefined action (submit or load) or custom actions you define on this form.
8149      * @param {String} actionName The name of the action type
8150      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8151      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8152      * accept other config options):
8153      * <pre>
8154 Property          Type             Description
8155 ----------------  ---------------  ----------------------------------------------------------------------------------
8156 url               String           The url for the action (defaults to the form's url)
8157 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8158 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8159 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8160                                    validate the form on the client (defaults to false)
8161      * </pre>
8162      * @return {BasicForm} this
8163      */
8164     doAction : function(action, options){
8165         if(typeof action == 'string'){
8166             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8167         }
8168         if(this.fireEvent('beforeaction', this, action) !== false){
8169             this.beforeAction(action);
8170             action.run.defer(100, action);
8171         }
8172         return this;
8173     },
8174
8175     // private
8176     beforeAction : function(action){
8177         var o = action.options;
8178         
8179         if(this.loadMask){
8180             
8181             if(this.maskBody){
8182                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8183             } else {
8184                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8185             }
8186         }
8187         // not really supported yet.. ??
8188
8189         //if(this.waitMsgTarget === true){
8190         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8191         //}else if(this.waitMsgTarget){
8192         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8193         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8194         //}else {
8195         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8196        // }
8197
8198     },
8199
8200     // private
8201     afterAction : function(action, success){
8202         this.activeAction = null;
8203         var o = action.options;
8204
8205         if(this.loadMask){
8206             
8207             if(this.maskBody){
8208                 Roo.get(document.body).unmask();
8209             } else {
8210                 this.el.unmask();
8211             }
8212         }
8213         
8214         //if(this.waitMsgTarget === true){
8215 //            this.el.unmask();
8216         //}else if(this.waitMsgTarget){
8217         //    this.waitMsgTarget.unmask();
8218         //}else{
8219         //    Roo.MessageBox.updateProgress(1);
8220         //    Roo.MessageBox.hide();
8221        // }
8222         //
8223         if(success){
8224             if(o.reset){
8225                 this.reset();
8226             }
8227             Roo.callback(o.success, o.scope, [this, action]);
8228             this.fireEvent('actioncomplete', this, action);
8229
8230         }else{
8231
8232             // failure condition..
8233             // we have a scenario where updates need confirming.
8234             // eg. if a locking scenario exists..
8235             // we look for { errors : { needs_confirm : true }} in the response.
8236             if (
8237                 (typeof(action.result) != 'undefined')  &&
8238                 (typeof(action.result.errors) != 'undefined')  &&
8239                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8240            ){
8241                 var _t = this;
8242                 Roo.log("not supported yet");
8243                  /*
8244
8245                 Roo.MessageBox.confirm(
8246                     "Change requires confirmation",
8247                     action.result.errorMsg,
8248                     function(r) {
8249                         if (r != 'yes') {
8250                             return;
8251                         }
8252                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8253                     }
8254
8255                 );
8256                 */
8257
8258
8259                 return;
8260             }
8261
8262             Roo.callback(o.failure, o.scope, [this, action]);
8263             // show an error message if no failed handler is set..
8264             if (!this.hasListener('actionfailed')) {
8265                 Roo.log("need to add dialog support");
8266                 /*
8267                 Roo.MessageBox.alert("Error",
8268                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8269                         action.result.errorMsg :
8270                         "Saving Failed, please check your entries or try again"
8271                 );
8272                 */
8273             }
8274
8275             this.fireEvent('actionfailed', this, action);
8276         }
8277
8278     },
8279     /**
8280      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8281      * @param {String} id The value to search for
8282      * @return Field
8283      */
8284     findField : function(id){
8285         var items = this.getItems();
8286         var field = items.get(id);
8287         if(!field){
8288              items.each(function(f){
8289                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8290                     field = f;
8291                     return false;
8292                 }
8293                 return true;
8294             });
8295         }
8296         return field || null;
8297     },
8298      /**
8299      * Mark fields in this form invalid in bulk.
8300      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8301      * @return {BasicForm} this
8302      */
8303     markInvalid : function(errors){
8304         if(errors instanceof Array){
8305             for(var i = 0, len = errors.length; i < len; i++){
8306                 var fieldError = errors[i];
8307                 var f = this.findField(fieldError.id);
8308                 if(f){
8309                     f.markInvalid(fieldError.msg);
8310                 }
8311             }
8312         }else{
8313             var field, id;
8314             for(id in errors){
8315                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8316                     field.markInvalid(errors[id]);
8317                 }
8318             }
8319         }
8320         //Roo.each(this.childForms || [], function (f) {
8321         //    f.markInvalid(errors);
8322         //});
8323
8324         return this;
8325     },
8326
8327     /**
8328      * Set values for fields in this form in bulk.
8329      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8330      * @return {BasicForm} this
8331      */
8332     setValues : function(values){
8333         if(values instanceof Array){ // array of objects
8334             for(var i = 0, len = values.length; i < len; i++){
8335                 var v = values[i];
8336                 var f = this.findField(v.id);
8337                 if(f){
8338                     f.setValue(v.value);
8339                     if(this.trackResetOnLoad){
8340                         f.originalValue = f.getValue();
8341                     }
8342                 }
8343             }
8344         }else{ // object hash
8345             var field, id;
8346             for(id in values){
8347                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8348
8349                     if (field.setFromData &&
8350                         field.valueField &&
8351                         field.displayField &&
8352                         // combos' with local stores can
8353                         // be queried via setValue()
8354                         // to set their value..
8355                         (field.store && !field.store.isLocal)
8356                         ) {
8357                         // it's a combo
8358                         var sd = { };
8359                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8360                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8361                         field.setFromData(sd);
8362
8363                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8364                         
8365                         field.setFromData(values);
8366                         
8367                     } else {
8368                         field.setValue(values[id]);
8369                     }
8370
8371
8372                     if(this.trackResetOnLoad){
8373                         field.originalValue = field.getValue();
8374                     }
8375                 }
8376             }
8377         }
8378
8379         //Roo.each(this.childForms || [], function (f) {
8380         //    f.setValues(values);
8381         //});
8382
8383         return this;
8384     },
8385
8386     /**
8387      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8388      * they are returned as an array.
8389      * @param {Boolean} asString
8390      * @return {Object}
8391      */
8392     getValues : function(asString){
8393         //if (this.childForms) {
8394             // copy values from the child forms
8395         //    Roo.each(this.childForms, function (f) {
8396         //        this.setValues(f.getValues());
8397         //    }, this);
8398         //}
8399
8400
8401
8402         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8403         if(asString === true){
8404             return fs;
8405         }
8406         return Roo.urlDecode(fs);
8407     },
8408
8409     /**
8410      * Returns the fields in this form as an object with key/value pairs.
8411      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8412      * @return {Object}
8413      */
8414     getFieldValues : function(with_hidden)
8415     {
8416         var items = this.getItems();
8417         var ret = {};
8418         items.each(function(f){
8419             
8420             if (!f.getName()) {
8421                 return;
8422             }
8423             
8424             var v = f.getValue();
8425             
8426             if (f.inputType =='radio') {
8427                 if (typeof(ret[f.getName()]) == 'undefined') {
8428                     ret[f.getName()] = ''; // empty..
8429                 }
8430
8431                 if (!f.el.dom.checked) {
8432                     return;
8433
8434                 }
8435                 v = f.el.dom.value;
8436
8437             }
8438             
8439             if(f.xtype == 'MoneyField'){
8440                 ret[f.currencyName] = f.getCurrency();
8441             }
8442
8443             // not sure if this supported any more..
8444             if ((typeof(v) == 'object') && f.getRawValue) {
8445                 v = f.getRawValue() ; // dates..
8446             }
8447             // combo boxes where name != hiddenName...
8448             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8449                 ret[f.name] = f.getRawValue();
8450             }
8451             ret[f.getName()] = v;
8452         });
8453
8454         return ret;
8455     },
8456
8457     /**
8458      * Clears all invalid messages in this form.
8459      * @return {BasicForm} this
8460      */
8461     clearInvalid : function(){
8462         var items = this.getItems();
8463
8464         items.each(function(f){
8465            f.clearInvalid();
8466         });
8467
8468         return this;
8469     },
8470
8471     /**
8472      * Resets this form.
8473      * @return {BasicForm} this
8474      */
8475     reset : function(){
8476         var items = this.getItems();
8477         items.each(function(f){
8478             f.reset();
8479         });
8480
8481         Roo.each(this.childForms || [], function (f) {
8482             f.reset();
8483         });
8484
8485
8486         return this;
8487     },
8488     
8489     getItems : function()
8490     {
8491         var r=new Roo.util.MixedCollection(false, function(o){
8492             return o.id || (o.id = Roo.id());
8493         });
8494         var iter = function(el) {
8495             if (el.inputEl) {
8496                 r.add(el);
8497             }
8498             if (!el.items) {
8499                 return;
8500             }
8501             Roo.each(el.items,function(e) {
8502                 iter(e);
8503             });
8504         };
8505
8506         iter(this);
8507         return r;
8508     },
8509     
8510     hideFields : function(items)
8511     {
8512         Roo.each(items, function(i){
8513             
8514             var f = this.findField(i);
8515             
8516             if(!f){
8517                 return;
8518             }
8519             
8520             f.hide();
8521             
8522         }, this);
8523     },
8524     
8525     showFields : function(items)
8526     {
8527         Roo.each(items, function(i){
8528             
8529             var f = this.findField(i);
8530             
8531             if(!f){
8532                 return;
8533             }
8534             
8535             f.show();
8536             
8537         }, this);
8538     }
8539
8540 });
8541
8542 Roo.apply(Roo.bootstrap.Form, {
8543     
8544     popover : {
8545         
8546         padding : 5,
8547         
8548         isApplied : false,
8549         
8550         isMasked : false,
8551         
8552         form : false,
8553         
8554         target : false,
8555         
8556         toolTip : false,
8557         
8558         intervalID : false,
8559         
8560         maskEl : false,
8561         
8562         apply : function()
8563         {
8564             if(this.isApplied){
8565                 return;
8566             }
8567             
8568             this.maskEl = {
8569                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8570                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8571                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8572                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8573             };
8574             
8575             this.maskEl.top.enableDisplayMode("block");
8576             this.maskEl.left.enableDisplayMode("block");
8577             this.maskEl.bottom.enableDisplayMode("block");
8578             this.maskEl.right.enableDisplayMode("block");
8579             
8580             this.toolTip = new Roo.bootstrap.Tooltip({
8581                 cls : 'roo-form-error-popover',
8582                 alignment : {
8583                     'left' : ['r-l', [-2,0], 'right'],
8584                     'right' : ['l-r', [2,0], 'left'],
8585                     'bottom' : ['tl-bl', [0,2], 'top'],
8586                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8587                 }
8588             });
8589             
8590             this.toolTip.render(Roo.get(document.body));
8591
8592             this.toolTip.el.enableDisplayMode("block");
8593             
8594             Roo.get(document.body).on('click', function(){
8595                 this.unmask();
8596             }, this);
8597             
8598             Roo.get(document.body).on('touchstart', function(){
8599                 this.unmask();
8600             }, this);
8601             
8602             this.isApplied = true
8603         },
8604         
8605         mask : function(form, target)
8606         {
8607             this.form = form;
8608             
8609             this.target = target;
8610             
8611             if(!this.form.errorMask || !target.el){
8612                 return;
8613             }
8614             
8615             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8616             
8617             Roo.log(scrollable);
8618             
8619             var ot = this.target.el.calcOffsetsTo(scrollable);
8620             
8621             var scrollTo = ot[1] - this.form.maskOffset;
8622             
8623             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8624             
8625             scrollable.scrollTo('top', scrollTo);
8626             
8627             var box = this.target.el.getBox();
8628             Roo.log(box);
8629             var zIndex = Roo.bootstrap.Modal.zIndex++;
8630
8631             
8632             this.maskEl.top.setStyle('position', 'absolute');
8633             this.maskEl.top.setStyle('z-index', zIndex);
8634             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8635             this.maskEl.top.setLeft(0);
8636             this.maskEl.top.setTop(0);
8637             this.maskEl.top.show();
8638             
8639             this.maskEl.left.setStyle('position', 'absolute');
8640             this.maskEl.left.setStyle('z-index', zIndex);
8641             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8642             this.maskEl.left.setLeft(0);
8643             this.maskEl.left.setTop(box.y - this.padding);
8644             this.maskEl.left.show();
8645
8646             this.maskEl.bottom.setStyle('position', 'absolute');
8647             this.maskEl.bottom.setStyle('z-index', zIndex);
8648             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8649             this.maskEl.bottom.setLeft(0);
8650             this.maskEl.bottom.setTop(box.bottom + this.padding);
8651             this.maskEl.bottom.show();
8652
8653             this.maskEl.right.setStyle('position', 'absolute');
8654             this.maskEl.right.setStyle('z-index', zIndex);
8655             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8656             this.maskEl.right.setLeft(box.right + this.padding);
8657             this.maskEl.right.setTop(box.y - this.padding);
8658             this.maskEl.right.show();
8659
8660             this.toolTip.bindEl = this.target.el;
8661
8662             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8663
8664             var tip = this.target.blankText;
8665
8666             if(this.target.getValue() !== '' ) {
8667                 
8668                 if (this.target.invalidText.length) {
8669                     tip = this.target.invalidText;
8670                 } else if (this.target.regexText.length){
8671                     tip = this.target.regexText;
8672                 }
8673             }
8674
8675             this.toolTip.show(tip);
8676
8677             this.intervalID = window.setInterval(function() {
8678                 Roo.bootstrap.Form.popover.unmask();
8679             }, 10000);
8680
8681             window.onwheel = function(){ return false;};
8682             
8683             (function(){ this.isMasked = true; }).defer(500, this);
8684             
8685         },
8686         
8687         unmask : function()
8688         {
8689             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8690                 return;
8691             }
8692             
8693             this.maskEl.top.setStyle('position', 'absolute');
8694             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8695             this.maskEl.top.hide();
8696
8697             this.maskEl.left.setStyle('position', 'absolute');
8698             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8699             this.maskEl.left.hide();
8700
8701             this.maskEl.bottom.setStyle('position', 'absolute');
8702             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8703             this.maskEl.bottom.hide();
8704
8705             this.maskEl.right.setStyle('position', 'absolute');
8706             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8707             this.maskEl.right.hide();
8708             
8709             this.toolTip.hide();
8710             
8711             this.toolTip.el.hide();
8712             
8713             window.onwheel = function(){ return true;};
8714             
8715             if(this.intervalID){
8716                 window.clearInterval(this.intervalID);
8717                 this.intervalID = false;
8718             }
8719             
8720             this.isMasked = false;
8721             
8722         }
8723         
8724     }
8725     
8726 });
8727
8728 /*
8729  * Based on:
8730  * Ext JS Library 1.1.1
8731  * Copyright(c) 2006-2007, Ext JS, LLC.
8732  *
8733  * Originally Released Under LGPL - original licence link has changed is not relivant.
8734  *
8735  * Fork - LGPL
8736  * <script type="text/javascript">
8737  */
8738 /**
8739  * @class Roo.form.VTypes
8740  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8741  * @singleton
8742  */
8743 Roo.form.VTypes = function(){
8744     // closure these in so they are only created once.
8745     var alpha = /^[a-zA-Z_]+$/;
8746     var alphanum = /^[a-zA-Z0-9_]+$/;
8747     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8748     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8749
8750     // All these messages and functions are configurable
8751     return {
8752         /**
8753          * The function used to validate email addresses
8754          * @param {String} value The email address
8755          */
8756         'email' : function(v){
8757             return email.test(v);
8758         },
8759         /**
8760          * The error text to display when the email validation function returns false
8761          * @type String
8762          */
8763         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8764         /**
8765          * The keystroke filter mask to be applied on email input
8766          * @type RegExp
8767          */
8768         'emailMask' : /[a-z0-9_\.\-@]/i,
8769
8770         /**
8771          * The function used to validate URLs
8772          * @param {String} value The URL
8773          */
8774         'url' : function(v){
8775             return url.test(v);
8776         },
8777         /**
8778          * The error text to display when the url validation function returns false
8779          * @type String
8780          */
8781         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8782         
8783         /**
8784          * The function used to validate alpha values
8785          * @param {String} value The value
8786          */
8787         'alpha' : function(v){
8788             return alpha.test(v);
8789         },
8790         /**
8791          * The error text to display when the alpha validation function returns false
8792          * @type String
8793          */
8794         'alphaText' : 'This field should only contain letters and _',
8795         /**
8796          * The keystroke filter mask to be applied on alpha input
8797          * @type RegExp
8798          */
8799         'alphaMask' : /[a-z_]/i,
8800
8801         /**
8802          * The function used to validate alphanumeric values
8803          * @param {String} value The value
8804          */
8805         'alphanum' : function(v){
8806             return alphanum.test(v);
8807         },
8808         /**
8809          * The error text to display when the alphanumeric validation function returns false
8810          * @type String
8811          */
8812         'alphanumText' : 'This field should only contain letters, numbers and _',
8813         /**
8814          * The keystroke filter mask to be applied on alphanumeric input
8815          * @type RegExp
8816          */
8817         'alphanumMask' : /[a-z0-9_]/i
8818     };
8819 }();/*
8820  * - LGPL
8821  *
8822  * Input
8823  * 
8824  */
8825
8826 /**
8827  * @class Roo.bootstrap.Input
8828  * @extends Roo.bootstrap.Component
8829  * Bootstrap Input class
8830  * @cfg {Boolean} disabled is it disabled
8831  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8832  * @cfg {String} name name of the input
8833  * @cfg {string} fieldLabel - the label associated
8834  * @cfg {string} placeholder - placeholder to put in text.
8835  * @cfg {string}  before - input group add on before
8836  * @cfg {string} after - input group add on after
8837  * @cfg {string} size - (lg|sm) or leave empty..
8838  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8839  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8840  * @cfg {Number} md colspan out of 12 for computer-sized screens
8841  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8842  * @cfg {string} value default value of the input
8843  * @cfg {Number} labelWidth set the width of label 
8844  * @cfg {Number} labellg set the width of label (1-12)
8845  * @cfg {Number} labelmd set the width of label (1-12)
8846  * @cfg {Number} labelsm set the width of label (1-12)
8847  * @cfg {Number} labelxs set the width of label (1-12)
8848  * @cfg {String} labelAlign (top|left)
8849  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8850  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8851  * @cfg {String} indicatorpos (left|right) default left
8852  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8853  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8854
8855  * @cfg {String} align (left|center|right) Default left
8856  * @cfg {Boolean} forceFeedback (true|false) Default false
8857  * 
8858  * @constructor
8859  * Create a new Input
8860  * @param {Object} config The config object
8861  */
8862
8863 Roo.bootstrap.Input = function(config){
8864     
8865     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8866     
8867     this.addEvents({
8868         /**
8869          * @event focus
8870          * Fires when this field receives input focus.
8871          * @param {Roo.form.Field} this
8872          */
8873         focus : true,
8874         /**
8875          * @event blur
8876          * Fires when this field loses input focus.
8877          * @param {Roo.form.Field} this
8878          */
8879         blur : true,
8880         /**
8881          * @event specialkey
8882          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8883          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8884          * @param {Roo.form.Field} this
8885          * @param {Roo.EventObject} e The event object
8886          */
8887         specialkey : true,
8888         /**
8889          * @event change
8890          * Fires just before the field blurs if the field value has changed.
8891          * @param {Roo.form.Field} this
8892          * @param {Mixed} newValue The new value
8893          * @param {Mixed} oldValue The original value
8894          */
8895         change : true,
8896         /**
8897          * @event invalid
8898          * Fires after the field has been marked as invalid.
8899          * @param {Roo.form.Field} this
8900          * @param {String} msg The validation message
8901          */
8902         invalid : true,
8903         /**
8904          * @event valid
8905          * Fires after the field has been validated with no errors.
8906          * @param {Roo.form.Field} this
8907          */
8908         valid : true,
8909          /**
8910          * @event keyup
8911          * Fires after the key up
8912          * @param {Roo.form.Field} this
8913          * @param {Roo.EventObject}  e The event Object
8914          */
8915         keyup : true
8916     });
8917 };
8918
8919 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8920      /**
8921      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8922       automatic validation (defaults to "keyup").
8923      */
8924     validationEvent : "keyup",
8925      /**
8926      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8927      */
8928     validateOnBlur : true,
8929     /**
8930      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8931      */
8932     validationDelay : 250,
8933      /**
8934      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8935      */
8936     focusClass : "x-form-focus",  // not needed???
8937     
8938        
8939     /**
8940      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8941      */
8942     invalidClass : "has-warning",
8943     
8944     /**
8945      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8946      */
8947     validClass : "has-success",
8948     
8949     /**
8950      * @cfg {Boolean} hasFeedback (true|false) default true
8951      */
8952     hasFeedback : true,
8953     
8954     /**
8955      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8956      */
8957     invalidFeedbackClass : "glyphicon-warning-sign",
8958     
8959     /**
8960      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8961      */
8962     validFeedbackClass : "glyphicon-ok",
8963     
8964     /**
8965      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8966      */
8967     selectOnFocus : false,
8968     
8969      /**
8970      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8971      */
8972     maskRe : null,
8973        /**
8974      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8975      */
8976     vtype : null,
8977     
8978       /**
8979      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8980      */
8981     disableKeyFilter : false,
8982     
8983        /**
8984      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8985      */
8986     disabled : false,
8987      /**
8988      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8989      */
8990     allowBlank : true,
8991     /**
8992      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8993      */
8994     blankText : "Please complete this mandatory field",
8995     
8996      /**
8997      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8998      */
8999     minLength : 0,
9000     /**
9001      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9002      */
9003     maxLength : Number.MAX_VALUE,
9004     /**
9005      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9006      */
9007     minLengthText : "The minimum length for this field is {0}",
9008     /**
9009      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9010      */
9011     maxLengthText : "The maximum length for this field is {0}",
9012   
9013     
9014     /**
9015      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9016      * If available, this function will be called only after the basic validators all return true, and will be passed the
9017      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9018      */
9019     validator : null,
9020     /**
9021      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9022      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9023      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9024      */
9025     regex : null,
9026     /**
9027      * @cfg {String} regexText -- Depricated - use Invalid Text
9028      */
9029     regexText : "",
9030     
9031     /**
9032      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9033      */
9034     invalidText : "",
9035     
9036     
9037     
9038     autocomplete: false,
9039     
9040     
9041     fieldLabel : '',
9042     inputType : 'text',
9043     
9044     name : false,
9045     placeholder: false,
9046     before : false,
9047     after : false,
9048     size : false,
9049     hasFocus : false,
9050     preventMark: false,
9051     isFormField : true,
9052     value : '',
9053     labelWidth : 2,
9054     labelAlign : false,
9055     readOnly : false,
9056     align : false,
9057     formatedValue : false,
9058     forceFeedback : false,
9059     
9060     indicatorpos : 'left',
9061     
9062     labellg : 0,
9063     labelmd : 0,
9064     labelsm : 0,
9065     labelxs : 0,
9066     
9067     capture : '',
9068     accept : '',
9069     
9070     parentLabelAlign : function()
9071     {
9072         var parent = this;
9073         while (parent.parent()) {
9074             parent = parent.parent();
9075             if (typeof(parent.labelAlign) !='undefined') {
9076                 return parent.labelAlign;
9077             }
9078         }
9079         return 'left';
9080         
9081     },
9082     
9083     getAutoCreate : function()
9084     {
9085         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9086         
9087         var id = Roo.id();
9088         
9089         var cfg = {};
9090         
9091         if(this.inputType != 'hidden'){
9092             cfg.cls = 'form-group' //input-group
9093         }
9094         
9095         var input =  {
9096             tag: 'input',
9097             id : id,
9098             type : this.inputType,
9099             value : this.value,
9100             cls : 'form-control',
9101             placeholder : this.placeholder || '',
9102             autocomplete : this.autocomplete || 'new-password'
9103         };
9104         
9105         if(this.capture.length){
9106             input.capture = this.capture;
9107         }
9108         
9109         if(this.accept.length){
9110             input.accept = this.accept + "/*";
9111         }
9112         
9113         if(this.align){
9114             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9115         }
9116         
9117         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9118             input.maxLength = this.maxLength;
9119         }
9120         
9121         if (this.disabled) {
9122             input.disabled=true;
9123         }
9124         
9125         if (this.readOnly) {
9126             input.readonly=true;
9127         }
9128         
9129         if (this.name) {
9130             input.name = this.name;
9131         }
9132         
9133         if (this.size) {
9134             input.cls += ' input-' + this.size;
9135         }
9136         
9137         var settings=this;
9138         ['xs','sm','md','lg'].map(function(size){
9139             if (settings[size]) {
9140                 cfg.cls += ' col-' + size + '-' + settings[size];
9141             }
9142         });
9143         
9144         var inputblock = input;
9145         
9146         var feedback = {
9147             tag: 'span',
9148             cls: 'glyphicon form-control-feedback'
9149         };
9150             
9151         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9152             
9153             inputblock = {
9154                 cls : 'has-feedback',
9155                 cn :  [
9156                     input,
9157                     feedback
9158                 ] 
9159             };  
9160         }
9161         
9162         if (this.before || this.after) {
9163             
9164             inputblock = {
9165                 cls : 'input-group',
9166                 cn :  [] 
9167             };
9168             
9169             if (this.before && typeof(this.before) == 'string') {
9170                 
9171                 inputblock.cn.push({
9172                     tag :'span',
9173                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9174                     html : this.before
9175                 });
9176             }
9177             if (this.before && typeof(this.before) == 'object') {
9178                 this.before = Roo.factory(this.before);
9179                 
9180                 inputblock.cn.push({
9181                     tag :'span',
9182                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9183                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9184                 });
9185             }
9186             
9187             inputblock.cn.push(input);
9188             
9189             if (this.after && typeof(this.after) == 'string') {
9190                 inputblock.cn.push({
9191                     tag :'span',
9192                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9193                     html : this.after
9194                 });
9195             }
9196             if (this.after && typeof(this.after) == 'object') {
9197                 this.after = Roo.factory(this.after);
9198                 
9199                 inputblock.cn.push({
9200                     tag :'span',
9201                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9202                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9203                 });
9204             }
9205             
9206             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9207                 inputblock.cls += ' has-feedback';
9208                 inputblock.cn.push(feedback);
9209             }
9210         };
9211         var indicator = {
9212             tag : 'i',
9213             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9214             tooltip : 'This field is required'
9215         };
9216         if (Roo.bootstrap.version == 4) {
9217             indicator = {
9218                 tag : 'i',
9219                 style : 'display-none'
9220             };
9221         }
9222         if (align ==='left' && this.fieldLabel.length) {
9223             
9224             cfg.cls += ' roo-form-group-label-left row';
9225             
9226             cfg.cn = [
9227                 indicator,
9228                 {
9229                     tag: 'label',
9230                     'for' :  id,
9231                     cls : 'control-label col-form-label',
9232                     html : this.fieldLabel
9233
9234                 },
9235                 {
9236                     cls : "", 
9237                     cn: [
9238                         inputblock
9239                     ]
9240                 }
9241             ];
9242             
9243             var labelCfg = cfg.cn[1];
9244             var contentCfg = cfg.cn[2];
9245             
9246             if(this.indicatorpos == 'right'){
9247                 cfg.cn = [
9248                     {
9249                         tag: 'label',
9250                         'for' :  id,
9251                         cls : 'control-label col-form-label',
9252                         cn : [
9253                             {
9254                                 tag : 'span',
9255                                 html : this.fieldLabel
9256                             },
9257                             indicator
9258                         ]
9259                     },
9260                     {
9261                         cls : "",
9262                         cn: [
9263                             inputblock
9264                         ]
9265                     }
9266
9267                 ];
9268                 
9269                 labelCfg = cfg.cn[0];
9270                 contentCfg = cfg.cn[1];
9271             
9272             }
9273             
9274             if(this.labelWidth > 12){
9275                 labelCfg.style = "width: " + this.labelWidth + 'px';
9276             }
9277             
9278             if(this.labelWidth < 13 && this.labelmd == 0){
9279                 this.labelmd = this.labelWidth;
9280             }
9281             
9282             if(this.labellg > 0){
9283                 labelCfg.cls += ' col-lg-' + this.labellg;
9284                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9285             }
9286             
9287             if(this.labelmd > 0){
9288                 labelCfg.cls += ' col-md-' + this.labelmd;
9289                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9290             }
9291             
9292             if(this.labelsm > 0){
9293                 labelCfg.cls += ' col-sm-' + this.labelsm;
9294                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9295             }
9296             
9297             if(this.labelxs > 0){
9298                 labelCfg.cls += ' col-xs-' + this.labelxs;
9299                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9300             }
9301             
9302             
9303         } else if ( this.fieldLabel.length) {
9304                 
9305             cfg.cn = [
9306                 {
9307                     tag : 'i',
9308                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9309                     tooltip : 'This field is required'
9310                 },
9311                 {
9312                     tag: 'label',
9313                    //cls : 'input-group-addon',
9314                     html : this.fieldLabel
9315
9316                 },
9317
9318                inputblock
9319
9320            ];
9321            
9322            if(this.indicatorpos == 'right'){
9323                 
9324                 cfg.cn = [
9325                     {
9326                         tag: 'label',
9327                        //cls : 'input-group-addon',
9328                         html : this.fieldLabel
9329
9330                     },
9331                     {
9332                         tag : 'i',
9333                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9334                         tooltip : 'This field is required'
9335                     },
9336
9337                    inputblock
9338
9339                ];
9340
9341             }
9342
9343         } else {
9344             
9345             cfg.cn = [
9346
9347                     inputblock
9348
9349             ];
9350                 
9351                 
9352         };
9353         
9354         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9355            cfg.cls += ' navbar-form';
9356         }
9357         
9358         if (this.parentType === 'NavGroup') {
9359            cfg.cls += ' navbar-form';
9360            cfg.tag = 'li';
9361         }
9362         
9363         return cfg;
9364         
9365     },
9366     /**
9367      * return the real input element.
9368      */
9369     inputEl: function ()
9370     {
9371         return this.el.select('input.form-control',true).first();
9372     },
9373     
9374     tooltipEl : function()
9375     {
9376         return this.inputEl();
9377     },
9378     
9379     indicatorEl : function()
9380     {
9381         if (Roo.bootstrap.version == 4) {
9382             return false; // not enabled in v4 yet.
9383         }
9384         
9385         var indicator = this.el.select('i.roo-required-indicator',true).first();
9386         
9387         if(!indicator){
9388             return false;
9389         }
9390         
9391         return indicator;
9392         
9393     },
9394     
9395     setDisabled : function(v)
9396     {
9397         var i  = this.inputEl().dom;
9398         if (!v) {
9399             i.removeAttribute('disabled');
9400             return;
9401             
9402         }
9403         i.setAttribute('disabled','true');
9404     },
9405     initEvents : function()
9406     {
9407           
9408         this.inputEl().on("keydown" , this.fireKey,  this);
9409         this.inputEl().on("focus", this.onFocus,  this);
9410         this.inputEl().on("blur", this.onBlur,  this);
9411         
9412         this.inputEl().relayEvent('keyup', this);
9413         
9414         this.indicator = this.indicatorEl();
9415         
9416         if(this.indicator){
9417             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9418         }
9419  
9420         // reference to original value for reset
9421         this.originalValue = this.getValue();
9422         //Roo.form.TextField.superclass.initEvents.call(this);
9423         if(this.validationEvent == 'keyup'){
9424             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9425             this.inputEl().on('keyup', this.filterValidation, this);
9426         }
9427         else if(this.validationEvent !== false){
9428             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9429         }
9430         
9431         if(this.selectOnFocus){
9432             this.on("focus", this.preFocus, this);
9433             
9434         }
9435         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9436             this.inputEl().on("keypress", this.filterKeys, this);
9437         } else {
9438             this.inputEl().relayEvent('keypress', this);
9439         }
9440        /* if(this.grow){
9441             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9442             this.el.on("click", this.autoSize,  this);
9443         }
9444         */
9445         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9446             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9447         }
9448         
9449         if (typeof(this.before) == 'object') {
9450             this.before.render(this.el.select('.roo-input-before',true).first());
9451         }
9452         if (typeof(this.after) == 'object') {
9453             this.after.render(this.el.select('.roo-input-after',true).first());
9454         }
9455         
9456         this.inputEl().on('change', this.onChange, this);
9457         
9458     },
9459     filterValidation : function(e){
9460         if(!e.isNavKeyPress()){
9461             this.validationTask.delay(this.validationDelay);
9462         }
9463     },
9464      /**
9465      * Validates the field value
9466      * @return {Boolean} True if the value is valid, else false
9467      */
9468     validate : function(){
9469         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9470         if(this.disabled || this.validateValue(this.getRawValue())){
9471             this.markValid();
9472             return true;
9473         }
9474         
9475         this.markInvalid();
9476         return false;
9477     },
9478     
9479     
9480     /**
9481      * Validates a value according to the field's validation rules and marks the field as invalid
9482      * if the validation fails
9483      * @param {Mixed} value The value to validate
9484      * @return {Boolean} True if the value is valid, else false
9485      */
9486     validateValue : function(value)
9487     {
9488         if(this.getVisibilityEl().hasClass('hidden')){
9489             return true;
9490         }
9491         
9492         if(value.length < 1)  { // if it's blank
9493             if(this.allowBlank){
9494                 return true;
9495             }
9496             return false;
9497         }
9498         
9499         if(value.length < this.minLength){
9500             return false;
9501         }
9502         if(value.length > this.maxLength){
9503             return false;
9504         }
9505         if(this.vtype){
9506             var vt = Roo.form.VTypes;
9507             if(!vt[this.vtype](value, this)){
9508                 return false;
9509             }
9510         }
9511         if(typeof this.validator == "function"){
9512             var msg = this.validator(value);
9513             if(msg !== true){
9514                 return false;
9515             }
9516             if (typeof(msg) == 'string') {
9517                 this.invalidText = msg;
9518             }
9519         }
9520         
9521         if(this.regex && !this.regex.test(value)){
9522             return false;
9523         }
9524         
9525         return true;
9526     },
9527     
9528      // private
9529     fireKey : function(e){
9530         //Roo.log('field ' + e.getKey());
9531         if(e.isNavKeyPress()){
9532             this.fireEvent("specialkey", this, e);
9533         }
9534     },
9535     focus : function (selectText){
9536         if(this.rendered){
9537             this.inputEl().focus();
9538             if(selectText === true){
9539                 this.inputEl().dom.select();
9540             }
9541         }
9542         return this;
9543     } ,
9544     
9545     onFocus : function(){
9546         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9547            // this.el.addClass(this.focusClass);
9548         }
9549         if(!this.hasFocus){
9550             this.hasFocus = true;
9551             this.startValue = this.getValue();
9552             this.fireEvent("focus", this);
9553         }
9554     },
9555     
9556     beforeBlur : Roo.emptyFn,
9557
9558     
9559     // private
9560     onBlur : function(){
9561         this.beforeBlur();
9562         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9563             //this.el.removeClass(this.focusClass);
9564         }
9565         this.hasFocus = false;
9566         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9567             this.validate();
9568         }
9569         var v = this.getValue();
9570         if(String(v) !== String(this.startValue)){
9571             this.fireEvent('change', this, v, this.startValue);
9572         }
9573         this.fireEvent("blur", this);
9574     },
9575     
9576     onChange : function(e)
9577     {
9578         var v = this.getValue();
9579         if(String(v) !== String(this.startValue)){
9580             this.fireEvent('change', this, v, this.startValue);
9581         }
9582         
9583     },
9584     
9585     /**
9586      * Resets the current field value to the originally loaded value and clears any validation messages
9587      */
9588     reset : function(){
9589         this.setValue(this.originalValue);
9590         this.validate();
9591     },
9592      /**
9593      * Returns the name of the field
9594      * @return {Mixed} name The name field
9595      */
9596     getName: function(){
9597         return this.name;
9598     },
9599      /**
9600      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9601      * @return {Mixed} value The field value
9602      */
9603     getValue : function(){
9604         
9605         var v = this.inputEl().getValue();
9606         
9607         return v;
9608     },
9609     /**
9610      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9611      * @return {Mixed} value The field value
9612      */
9613     getRawValue : function(){
9614         var v = this.inputEl().getValue();
9615         
9616         return v;
9617     },
9618     
9619     /**
9620      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9621      * @param {Mixed} value The value to set
9622      */
9623     setRawValue : function(v){
9624         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9625     },
9626     
9627     selectText : function(start, end){
9628         var v = this.getRawValue();
9629         if(v.length > 0){
9630             start = start === undefined ? 0 : start;
9631             end = end === undefined ? v.length : end;
9632             var d = this.inputEl().dom;
9633             if(d.setSelectionRange){
9634                 d.setSelectionRange(start, end);
9635             }else if(d.createTextRange){
9636                 var range = d.createTextRange();
9637                 range.moveStart("character", start);
9638                 range.moveEnd("character", v.length-end);
9639                 range.select();
9640             }
9641         }
9642     },
9643     
9644     /**
9645      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9646      * @param {Mixed} value The value to set
9647      */
9648     setValue : function(v){
9649         this.value = v;
9650         if(this.rendered){
9651             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9652             this.validate();
9653         }
9654     },
9655     
9656     /*
9657     processValue : function(value){
9658         if(this.stripCharsRe){
9659             var newValue = value.replace(this.stripCharsRe, '');
9660             if(newValue !== value){
9661                 this.setRawValue(newValue);
9662                 return newValue;
9663             }
9664         }
9665         return value;
9666     },
9667   */
9668     preFocus : function(){
9669         
9670         if(this.selectOnFocus){
9671             this.inputEl().dom.select();
9672         }
9673     },
9674     filterKeys : function(e){
9675         var k = e.getKey();
9676         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9677             return;
9678         }
9679         var c = e.getCharCode(), cc = String.fromCharCode(c);
9680         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9681             return;
9682         }
9683         if(!this.maskRe.test(cc)){
9684             e.stopEvent();
9685         }
9686     },
9687      /**
9688      * Clear any invalid styles/messages for this field
9689      */
9690     clearInvalid : function(){
9691         
9692         if(!this.el || this.preventMark){ // not rendered
9693             return;
9694         }
9695         
9696      
9697         this.el.removeClass(this.invalidClass);
9698         
9699         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9700             
9701             var feedback = this.el.select('.form-control-feedback', true).first();
9702             
9703             if(feedback){
9704                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9705             }
9706             
9707         }
9708         
9709         if(this.indicator){
9710             this.indicator.removeClass('visible');
9711             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9712         }
9713         
9714         this.fireEvent('valid', this);
9715     },
9716     
9717      /**
9718      * Mark this field as valid
9719      */
9720     markValid : function()
9721     {
9722         if(!this.el  || this.preventMark){ // not rendered...
9723             return;
9724         }
9725         
9726         this.el.removeClass([this.invalidClass, this.validClass]);
9727         
9728         var feedback = this.el.select('.form-control-feedback', true).first();
9729             
9730         if(feedback){
9731             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9732         }
9733         
9734         if(this.indicator){
9735             this.indicator.removeClass('visible');
9736             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9737         }
9738         
9739         if(this.disabled){
9740             return;
9741         }
9742         
9743         if(this.allowBlank && !this.getRawValue().length){
9744             return;
9745         }
9746         
9747         this.el.addClass(this.validClass);
9748         
9749         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9750             
9751             var feedback = this.el.select('.form-control-feedback', true).first();
9752             
9753             if(feedback){
9754                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9755                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9756             }
9757             
9758         }
9759         
9760         this.fireEvent('valid', this);
9761     },
9762     
9763      /**
9764      * Mark this field as invalid
9765      * @param {String} msg The validation message
9766      */
9767     markInvalid : function(msg)
9768     {
9769         if(!this.el  || this.preventMark){ // not rendered
9770             return;
9771         }
9772         
9773         this.el.removeClass([this.invalidClass, this.validClass]);
9774         
9775         var feedback = this.el.select('.form-control-feedback', true).first();
9776             
9777         if(feedback){
9778             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9779         }
9780
9781         if(this.disabled){
9782             return;
9783         }
9784         
9785         if(this.allowBlank && !this.getRawValue().length){
9786             return;
9787         }
9788         
9789         if(this.indicator){
9790             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9791             this.indicator.addClass('visible');
9792         }
9793         
9794         this.el.addClass(this.invalidClass);
9795         
9796         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9797             
9798             var feedback = this.el.select('.form-control-feedback', true).first();
9799             
9800             if(feedback){
9801                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9802                 
9803                 if(this.getValue().length || this.forceFeedback){
9804                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9805                 }
9806                 
9807             }
9808             
9809         }
9810         
9811         this.fireEvent('invalid', this, msg);
9812     },
9813     // private
9814     SafariOnKeyDown : function(event)
9815     {
9816         // this is a workaround for a password hang bug on chrome/ webkit.
9817         if (this.inputEl().dom.type != 'password') {
9818             return;
9819         }
9820         
9821         var isSelectAll = false;
9822         
9823         if(this.inputEl().dom.selectionEnd > 0){
9824             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9825         }
9826         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9827             event.preventDefault();
9828             this.setValue('');
9829             return;
9830         }
9831         
9832         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9833             
9834             event.preventDefault();
9835             // this is very hacky as keydown always get's upper case.
9836             //
9837             var cc = String.fromCharCode(event.getCharCode());
9838             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9839             
9840         }
9841     },
9842     adjustWidth : function(tag, w){
9843         tag = tag.toLowerCase();
9844         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9845             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9846                 if(tag == 'input'){
9847                     return w + 2;
9848                 }
9849                 if(tag == 'textarea'){
9850                     return w-2;
9851                 }
9852             }else if(Roo.isOpera){
9853                 if(tag == 'input'){
9854                     return w + 2;
9855                 }
9856                 if(tag == 'textarea'){
9857                     return w-2;
9858                 }
9859             }
9860         }
9861         return w;
9862     },
9863     
9864     setFieldLabel : function(v)
9865     {
9866         if(!this.rendered){
9867             return;
9868         }
9869         
9870         if(this.indicatorEl()){
9871             var ar = this.el.select('label > span',true);
9872             
9873             if (ar.elements.length) {
9874                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9875                 this.fieldLabel = v;
9876                 return;
9877             }
9878             
9879             var br = this.el.select('label',true);
9880             
9881             if(br.elements.length) {
9882                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9883                 this.fieldLabel = v;
9884                 return;
9885             }
9886             
9887             Roo.log('Cannot Found any of label > span || label in input');
9888             return;
9889         }
9890         
9891         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9892         this.fieldLabel = v;
9893         
9894         
9895     }
9896 });
9897
9898  
9899 /*
9900  * - LGPL
9901  *
9902  * Input
9903  * 
9904  */
9905
9906 /**
9907  * @class Roo.bootstrap.TextArea
9908  * @extends Roo.bootstrap.Input
9909  * Bootstrap TextArea class
9910  * @cfg {Number} cols Specifies the visible width of a text area
9911  * @cfg {Number} rows Specifies the visible number of lines in a text area
9912  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9913  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9914  * @cfg {string} html text
9915  * 
9916  * @constructor
9917  * Create a new TextArea
9918  * @param {Object} config The config object
9919  */
9920
9921 Roo.bootstrap.TextArea = function(config){
9922     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9923    
9924 };
9925
9926 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9927      
9928     cols : false,
9929     rows : 5,
9930     readOnly : false,
9931     warp : 'soft',
9932     resize : false,
9933     value: false,
9934     html: false,
9935     
9936     getAutoCreate : function(){
9937         
9938         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9939         
9940         var id = Roo.id();
9941         
9942         var cfg = {};
9943         
9944         if(this.inputType != 'hidden'){
9945             cfg.cls = 'form-group' //input-group
9946         }
9947         
9948         var input =  {
9949             tag: 'textarea',
9950             id : id,
9951             warp : this.warp,
9952             rows : this.rows,
9953             value : this.value || '',
9954             html: this.html || '',
9955             cls : 'form-control',
9956             placeholder : this.placeholder || '' 
9957             
9958         };
9959         
9960         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9961             input.maxLength = this.maxLength;
9962         }
9963         
9964         if(this.resize){
9965             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9966         }
9967         
9968         if(this.cols){
9969             input.cols = this.cols;
9970         }
9971         
9972         if (this.readOnly) {
9973             input.readonly = true;
9974         }
9975         
9976         if (this.name) {
9977             input.name = this.name;
9978         }
9979         
9980         if (this.size) {
9981             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9982         }
9983         
9984         var settings=this;
9985         ['xs','sm','md','lg'].map(function(size){
9986             if (settings[size]) {
9987                 cfg.cls += ' col-' + size + '-' + settings[size];
9988             }
9989         });
9990         
9991         var inputblock = input;
9992         
9993         if(this.hasFeedback && !this.allowBlank){
9994             
9995             var feedback = {
9996                 tag: 'span',
9997                 cls: 'glyphicon form-control-feedback'
9998             };
9999
10000             inputblock = {
10001                 cls : 'has-feedback',
10002                 cn :  [
10003                     input,
10004                     feedback
10005                 ] 
10006             };  
10007         }
10008         
10009         
10010         if (this.before || this.after) {
10011             
10012             inputblock = {
10013                 cls : 'input-group',
10014                 cn :  [] 
10015             };
10016             if (this.before) {
10017                 inputblock.cn.push({
10018                     tag :'span',
10019                     cls : 'input-group-addon',
10020                     html : this.before
10021                 });
10022             }
10023             
10024             inputblock.cn.push(input);
10025             
10026             if(this.hasFeedback && !this.allowBlank){
10027                 inputblock.cls += ' has-feedback';
10028                 inputblock.cn.push(feedback);
10029             }
10030             
10031             if (this.after) {
10032                 inputblock.cn.push({
10033                     tag :'span',
10034                     cls : 'input-group-addon',
10035                     html : this.after
10036                 });
10037             }
10038             
10039         }
10040         
10041         if (align ==='left' && this.fieldLabel.length) {
10042             cfg.cn = [
10043                 {
10044                     tag: 'label',
10045                     'for' :  id,
10046                     cls : 'control-label',
10047                     html : this.fieldLabel
10048                 },
10049                 {
10050                     cls : "",
10051                     cn: [
10052                         inputblock
10053                     ]
10054                 }
10055
10056             ];
10057             
10058             if(this.labelWidth > 12){
10059                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10060             }
10061
10062             if(this.labelWidth < 13 && this.labelmd == 0){
10063                 this.labelmd = this.labelWidth;
10064             }
10065
10066             if(this.labellg > 0){
10067                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10068                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10069             }
10070
10071             if(this.labelmd > 0){
10072                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10073                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10074             }
10075
10076             if(this.labelsm > 0){
10077                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10078                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10079             }
10080
10081             if(this.labelxs > 0){
10082                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10083                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10084             }
10085             
10086         } else if ( this.fieldLabel.length) {
10087             cfg.cn = [
10088
10089                {
10090                    tag: 'label',
10091                    //cls : 'input-group-addon',
10092                    html : this.fieldLabel
10093
10094                },
10095
10096                inputblock
10097
10098            ];
10099
10100         } else {
10101
10102             cfg.cn = [
10103
10104                 inputblock
10105
10106             ];
10107                 
10108         }
10109         
10110         if (this.disabled) {
10111             input.disabled=true;
10112         }
10113         
10114         return cfg;
10115         
10116     },
10117     /**
10118      * return the real textarea element.
10119      */
10120     inputEl: function ()
10121     {
10122         return this.el.select('textarea.form-control',true).first();
10123     },
10124     
10125     /**
10126      * Clear any invalid styles/messages for this field
10127      */
10128     clearInvalid : function()
10129     {
10130         
10131         if(!this.el || this.preventMark){ // not rendered
10132             return;
10133         }
10134         
10135         var label = this.el.select('label', true).first();
10136         var icon = this.el.select('i.fa-star', true).first();
10137         
10138         if(label && icon){
10139             icon.remove();
10140         }
10141         
10142         this.el.removeClass(this.invalidClass);
10143         
10144         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10145             
10146             var feedback = this.el.select('.form-control-feedback', true).first();
10147             
10148             if(feedback){
10149                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10150             }
10151             
10152         }
10153         
10154         this.fireEvent('valid', this);
10155     },
10156     
10157      /**
10158      * Mark this field as valid
10159      */
10160     markValid : function()
10161     {
10162         if(!this.el  || this.preventMark){ // not rendered
10163             return;
10164         }
10165         
10166         this.el.removeClass([this.invalidClass, this.validClass]);
10167         
10168         var feedback = this.el.select('.form-control-feedback', true).first();
10169             
10170         if(feedback){
10171             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10172         }
10173
10174         if(this.disabled || this.allowBlank){
10175             return;
10176         }
10177         
10178         var label = this.el.select('label', true).first();
10179         var icon = this.el.select('i.fa-star', true).first();
10180         
10181         if(label && icon){
10182             icon.remove();
10183         }
10184         
10185         this.el.addClass(this.validClass);
10186         
10187         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10188             
10189             var feedback = this.el.select('.form-control-feedback', true).first();
10190             
10191             if(feedback){
10192                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10193                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10194             }
10195             
10196         }
10197         
10198         this.fireEvent('valid', this);
10199     },
10200     
10201      /**
10202      * Mark this field as invalid
10203      * @param {String} msg The validation message
10204      */
10205     markInvalid : function(msg)
10206     {
10207         if(!this.el  || this.preventMark){ // not rendered
10208             return;
10209         }
10210         
10211         this.el.removeClass([this.invalidClass, this.validClass]);
10212         
10213         var feedback = this.el.select('.form-control-feedback', true).first();
10214             
10215         if(feedback){
10216             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10217         }
10218
10219         if(this.disabled || this.allowBlank){
10220             return;
10221         }
10222         
10223         var label = this.el.select('label', true).first();
10224         var icon = this.el.select('i.fa-star', true).first();
10225         
10226         if(!this.getValue().length && label && !icon){
10227             this.el.createChild({
10228                 tag : 'i',
10229                 cls : 'text-danger fa fa-lg fa-star',
10230                 tooltip : 'This field is required',
10231                 style : 'margin-right:5px;'
10232             }, label, true);
10233         }
10234
10235         this.el.addClass(this.invalidClass);
10236         
10237         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10238             
10239             var feedback = this.el.select('.form-control-feedback', true).first();
10240             
10241             if(feedback){
10242                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10243                 
10244                 if(this.getValue().length || this.forceFeedback){
10245                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10246                 }
10247                 
10248             }
10249             
10250         }
10251         
10252         this.fireEvent('invalid', this, msg);
10253     }
10254 });
10255
10256  
10257 /*
10258  * - LGPL
10259  *
10260  * trigger field - base class for combo..
10261  * 
10262  */
10263  
10264 /**
10265  * @class Roo.bootstrap.TriggerField
10266  * @extends Roo.bootstrap.Input
10267  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10268  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10269  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10270  * for which you can provide a custom implementation.  For example:
10271  * <pre><code>
10272 var trigger = new Roo.bootstrap.TriggerField();
10273 trigger.onTriggerClick = myTriggerFn;
10274 trigger.applyTo('my-field');
10275 </code></pre>
10276  *
10277  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10278  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10279  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10280  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10281  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10282
10283  * @constructor
10284  * Create a new TriggerField.
10285  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10286  * to the base TextField)
10287  */
10288 Roo.bootstrap.TriggerField = function(config){
10289     this.mimicing = false;
10290     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10291 };
10292
10293 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10294     /**
10295      * @cfg {String} triggerClass A CSS class to apply to the trigger
10296      */
10297      /**
10298      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10299      */
10300     hideTrigger:false,
10301
10302     /**
10303      * @cfg {Boolean} removable (true|false) special filter default false
10304      */
10305     removable : false,
10306     
10307     /** @cfg {Boolean} grow @hide */
10308     /** @cfg {Number} growMin @hide */
10309     /** @cfg {Number} growMax @hide */
10310
10311     /**
10312      * @hide 
10313      * @method
10314      */
10315     autoSize: Roo.emptyFn,
10316     // private
10317     monitorTab : true,
10318     // private
10319     deferHeight : true,
10320
10321     
10322     actionMode : 'wrap',
10323     
10324     caret : false,
10325     
10326     
10327     getAutoCreate : function(){
10328        
10329         var align = this.labelAlign || this.parentLabelAlign();
10330         
10331         var id = Roo.id();
10332         
10333         var cfg = {
10334             cls: 'form-group' //input-group
10335         };
10336         
10337         
10338         var input =  {
10339             tag: 'input',
10340             id : id,
10341             type : this.inputType,
10342             cls : 'form-control',
10343             autocomplete: 'new-password',
10344             placeholder : this.placeholder || '' 
10345             
10346         };
10347         if (this.name) {
10348             input.name = this.name;
10349         }
10350         if (this.size) {
10351             input.cls += ' input-' + this.size;
10352         }
10353         
10354         if (this.disabled) {
10355             input.disabled=true;
10356         }
10357         
10358         var inputblock = input;
10359         
10360         if(this.hasFeedback && !this.allowBlank){
10361             
10362             var feedback = {
10363                 tag: 'span',
10364                 cls: 'glyphicon form-control-feedback'
10365             };
10366             
10367             if(this.removable && !this.editable && !this.tickable){
10368                 inputblock = {
10369                     cls : 'has-feedback',
10370                     cn :  [
10371                         inputblock,
10372                         {
10373                             tag: 'button',
10374                             html : 'x',
10375                             cls : 'roo-combo-removable-btn close'
10376                         },
10377                         feedback
10378                     ] 
10379                 };
10380             } else {
10381                 inputblock = {
10382                     cls : 'has-feedback',
10383                     cn :  [
10384                         inputblock,
10385                         feedback
10386                     ] 
10387                 };
10388             }
10389
10390         } else {
10391             if(this.removable && !this.editable && !this.tickable){
10392                 inputblock = {
10393                     cls : 'roo-removable',
10394                     cn :  [
10395                         inputblock,
10396                         {
10397                             tag: 'button',
10398                             html : 'x',
10399                             cls : 'roo-combo-removable-btn close'
10400                         }
10401                     ] 
10402                 };
10403             }
10404         }
10405         
10406         if (this.before || this.after) {
10407             
10408             inputblock = {
10409                 cls : 'input-group',
10410                 cn :  [] 
10411             };
10412             if (this.before) {
10413                 inputblock.cn.push({
10414                     tag :'span',
10415                     cls : 'input-group-addon input-group-prepend input-group-text',
10416                     html : this.before
10417                 });
10418             }
10419             
10420             inputblock.cn.push(input);
10421             
10422             if(this.hasFeedback && !this.allowBlank){
10423                 inputblock.cls += ' has-feedback';
10424                 inputblock.cn.push(feedback);
10425             }
10426             
10427             if (this.after) {
10428                 inputblock.cn.push({
10429                     tag :'span',
10430                     cls : 'input-group-addon input-group-append input-group-text',
10431                     html : this.after
10432                 });
10433             }
10434             
10435         };
10436         
10437       
10438         
10439         var ibwrap = inputblock;
10440         
10441         if(this.multiple){
10442             ibwrap = {
10443                 tag: 'ul',
10444                 cls: 'roo-select2-choices',
10445                 cn:[
10446                     {
10447                         tag: 'li',
10448                         cls: 'roo-select2-search-field',
10449                         cn: [
10450
10451                             inputblock
10452                         ]
10453                     }
10454                 ]
10455             };
10456                 
10457         }
10458         
10459         var combobox = {
10460             cls: 'roo-select2-container input-group',
10461             cn: [
10462                  {
10463                     tag: 'input',
10464                     type : 'hidden',
10465                     cls: 'form-hidden-field'
10466                 },
10467                 ibwrap
10468             ]
10469         };
10470         
10471         if(!this.multiple && this.showToggleBtn){
10472             
10473             var caret = {
10474                         tag: 'span',
10475                         cls: 'caret'
10476              };
10477             if (this.caret != false) {
10478                 caret = {
10479                      tag: 'i',
10480                      cls: 'fa fa-' + this.caret
10481                 };
10482                 
10483             }
10484             
10485             combobox.cn.push({
10486                 tag :'span',
10487                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10488                 cn : [
10489                     caret,
10490                     {
10491                         tag: 'span',
10492                         cls: 'combobox-clear',
10493                         cn  : [
10494                             {
10495                                 tag : 'i',
10496                                 cls: 'icon-remove'
10497                             }
10498                         ]
10499                     }
10500                 ]
10501
10502             })
10503         }
10504         
10505         if(this.multiple){
10506             combobox.cls += ' roo-select2-container-multi';
10507         }
10508          var indicator = {
10509             tag : 'i',
10510             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10511             tooltip : 'This field is required'
10512         };
10513         if (Roo.bootstrap.version == 4) {
10514             indicator = {
10515                 tag : 'i',
10516                 style : 'display:none'
10517             };
10518         }
10519         
10520         
10521         if (align ==='left' && this.fieldLabel.length) {
10522             
10523             cfg.cls += ' roo-form-group-label-left row';
10524
10525             cfg.cn = [
10526                 indicator,
10527                 {
10528                     tag: 'label',
10529                     'for' :  id,
10530                     cls : 'control-label',
10531                     html : this.fieldLabel
10532
10533                 },
10534                 {
10535                     cls : "", 
10536                     cn: [
10537                         combobox
10538                     ]
10539                 }
10540
10541             ];
10542             
10543             var labelCfg = cfg.cn[1];
10544             var contentCfg = cfg.cn[2];
10545             
10546             if(this.indicatorpos == 'right'){
10547                 cfg.cn = [
10548                     {
10549                         tag: 'label',
10550                         'for' :  id,
10551                         cls : 'control-label',
10552                         cn : [
10553                             {
10554                                 tag : 'span',
10555                                 html : this.fieldLabel
10556                             },
10557                             indicator
10558                         ]
10559                     },
10560                     {
10561                         cls : "", 
10562                         cn: [
10563                             combobox
10564                         ]
10565                     }
10566
10567                 ];
10568                 
10569                 labelCfg = cfg.cn[0];
10570                 contentCfg = cfg.cn[1];
10571             }
10572             
10573             if(this.labelWidth > 12){
10574                 labelCfg.style = "width: " + this.labelWidth + 'px';
10575             }
10576             
10577             if(this.labelWidth < 13 && this.labelmd == 0){
10578                 this.labelmd = this.labelWidth;
10579             }
10580             
10581             if(this.labellg > 0){
10582                 labelCfg.cls += ' col-lg-' + this.labellg;
10583                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10584             }
10585             
10586             if(this.labelmd > 0){
10587                 labelCfg.cls += ' col-md-' + this.labelmd;
10588                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10589             }
10590             
10591             if(this.labelsm > 0){
10592                 labelCfg.cls += ' col-sm-' + this.labelsm;
10593                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10594             }
10595             
10596             if(this.labelxs > 0){
10597                 labelCfg.cls += ' col-xs-' + this.labelxs;
10598                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10599             }
10600             
10601         } else if ( this.fieldLabel.length) {
10602 //                Roo.log(" label");
10603             cfg.cn = [
10604                 indicator,
10605                {
10606                    tag: 'label',
10607                    //cls : 'input-group-addon',
10608                    html : this.fieldLabel
10609
10610                },
10611
10612                combobox
10613
10614             ];
10615             
10616             if(this.indicatorpos == 'right'){
10617                 
10618                 cfg.cn = [
10619                     {
10620                        tag: 'label',
10621                        cn : [
10622                            {
10623                                tag : 'span',
10624                                html : this.fieldLabel
10625                            },
10626                            indicator
10627                        ]
10628
10629                     },
10630                     combobox
10631
10632                 ];
10633
10634             }
10635
10636         } else {
10637             
10638 //                Roo.log(" no label && no align");
10639                 cfg = combobox
10640                      
10641                 
10642         }
10643         
10644         var settings=this;
10645         ['xs','sm','md','lg'].map(function(size){
10646             if (settings[size]) {
10647                 cfg.cls += ' col-' + size + '-' + settings[size];
10648             }
10649         });
10650         
10651         return cfg;
10652         
10653     },
10654     
10655     
10656     
10657     // private
10658     onResize : function(w, h){
10659 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10660 //        if(typeof w == 'number'){
10661 //            var x = w - this.trigger.getWidth();
10662 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10663 //            this.trigger.setStyle('left', x+'px');
10664 //        }
10665     },
10666
10667     // private
10668     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10669
10670     // private
10671     getResizeEl : function(){
10672         return this.inputEl();
10673     },
10674
10675     // private
10676     getPositionEl : function(){
10677         return this.inputEl();
10678     },
10679
10680     // private
10681     alignErrorIcon : function(){
10682         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10683     },
10684
10685     // private
10686     initEvents : function(){
10687         
10688         this.createList();
10689         
10690         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10691         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10692         if(!this.multiple && this.showToggleBtn){
10693             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10694             if(this.hideTrigger){
10695                 this.trigger.setDisplayed(false);
10696             }
10697             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10698         }
10699         
10700         if(this.multiple){
10701             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10702         }
10703         
10704         if(this.removable && !this.editable && !this.tickable){
10705             var close = this.closeTriggerEl();
10706             
10707             if(close){
10708                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10709                 close.on('click', this.removeBtnClick, this, close);
10710             }
10711         }
10712         
10713         //this.trigger.addClassOnOver('x-form-trigger-over');
10714         //this.trigger.addClassOnClick('x-form-trigger-click');
10715         
10716         //if(!this.width){
10717         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10718         //}
10719     },
10720     
10721     closeTriggerEl : function()
10722     {
10723         var close = this.el.select('.roo-combo-removable-btn', true).first();
10724         return close ? close : false;
10725     },
10726     
10727     removeBtnClick : function(e, h, el)
10728     {
10729         e.preventDefault();
10730         
10731         if(this.fireEvent("remove", this) !== false){
10732             this.reset();
10733             this.fireEvent("afterremove", this)
10734         }
10735     },
10736     
10737     createList : function()
10738     {
10739         this.list = Roo.get(document.body).createChild({
10740             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10741             cls: 'typeahead typeahead-long dropdown-menu',
10742             style: 'display:none'
10743         });
10744         
10745         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10746         
10747     },
10748
10749     // private
10750     initTrigger : function(){
10751        
10752     },
10753
10754     // private
10755     onDestroy : function(){
10756         if(this.trigger){
10757             this.trigger.removeAllListeners();
10758           //  this.trigger.remove();
10759         }
10760         //if(this.wrap){
10761         //    this.wrap.remove();
10762         //}
10763         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10764     },
10765
10766     // private
10767     onFocus : function(){
10768         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10769         /*
10770         if(!this.mimicing){
10771             this.wrap.addClass('x-trigger-wrap-focus');
10772             this.mimicing = true;
10773             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10774             if(this.monitorTab){
10775                 this.el.on("keydown", this.checkTab, this);
10776             }
10777         }
10778         */
10779     },
10780
10781     // private
10782     checkTab : function(e){
10783         if(e.getKey() == e.TAB){
10784             this.triggerBlur();
10785         }
10786     },
10787
10788     // private
10789     onBlur : function(){
10790         // do nothing
10791     },
10792
10793     // private
10794     mimicBlur : function(e, t){
10795         /*
10796         if(!this.wrap.contains(t) && this.validateBlur()){
10797             this.triggerBlur();
10798         }
10799         */
10800     },
10801
10802     // private
10803     triggerBlur : function(){
10804         this.mimicing = false;
10805         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10806         if(this.monitorTab){
10807             this.el.un("keydown", this.checkTab, this);
10808         }
10809         //this.wrap.removeClass('x-trigger-wrap-focus');
10810         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10811     },
10812
10813     // private
10814     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10815     validateBlur : function(e, t){
10816         return true;
10817     },
10818
10819     // private
10820     onDisable : function(){
10821         this.inputEl().dom.disabled = true;
10822         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10823         //if(this.wrap){
10824         //    this.wrap.addClass('x-item-disabled');
10825         //}
10826     },
10827
10828     // private
10829     onEnable : function(){
10830         this.inputEl().dom.disabled = false;
10831         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10832         //if(this.wrap){
10833         //    this.el.removeClass('x-item-disabled');
10834         //}
10835     },
10836
10837     // private
10838     onShow : function(){
10839         var ae = this.getActionEl();
10840         
10841         if(ae){
10842             ae.dom.style.display = '';
10843             ae.dom.style.visibility = 'visible';
10844         }
10845     },
10846
10847     // private
10848     
10849     onHide : function(){
10850         var ae = this.getActionEl();
10851         ae.dom.style.display = 'none';
10852     },
10853
10854     /**
10855      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10856      * by an implementing function.
10857      * @method
10858      * @param {EventObject} e
10859      */
10860     onTriggerClick : Roo.emptyFn
10861 });
10862  /*
10863  * Based on:
10864  * Ext JS Library 1.1.1
10865  * Copyright(c) 2006-2007, Ext JS, LLC.
10866  *
10867  * Originally Released Under LGPL - original licence link has changed is not relivant.
10868  *
10869  * Fork - LGPL
10870  * <script type="text/javascript">
10871  */
10872
10873
10874 /**
10875  * @class Roo.data.SortTypes
10876  * @singleton
10877  * Defines the default sorting (casting?) comparison functions used when sorting data.
10878  */
10879 Roo.data.SortTypes = {
10880     /**
10881      * Default sort that does nothing
10882      * @param {Mixed} s The value being converted
10883      * @return {Mixed} The comparison value
10884      */
10885     none : function(s){
10886         return s;
10887     },
10888     
10889     /**
10890      * The regular expression used to strip tags
10891      * @type {RegExp}
10892      * @property
10893      */
10894     stripTagsRE : /<\/?[^>]+>/gi,
10895     
10896     /**
10897      * Strips all HTML tags to sort on text only
10898      * @param {Mixed} s The value being converted
10899      * @return {String} The comparison value
10900      */
10901     asText : function(s){
10902         return String(s).replace(this.stripTagsRE, "");
10903     },
10904     
10905     /**
10906      * Strips all HTML tags to sort on text only - Case insensitive
10907      * @param {Mixed} s The value being converted
10908      * @return {String} The comparison value
10909      */
10910     asUCText : function(s){
10911         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10912     },
10913     
10914     /**
10915      * Case insensitive string
10916      * @param {Mixed} s The value being converted
10917      * @return {String} The comparison value
10918      */
10919     asUCString : function(s) {
10920         return String(s).toUpperCase();
10921     },
10922     
10923     /**
10924      * Date sorting
10925      * @param {Mixed} s The value being converted
10926      * @return {Number} The comparison value
10927      */
10928     asDate : function(s) {
10929         if(!s){
10930             return 0;
10931         }
10932         if(s instanceof Date){
10933             return s.getTime();
10934         }
10935         return Date.parse(String(s));
10936     },
10937     
10938     /**
10939      * Float sorting
10940      * @param {Mixed} s The value being converted
10941      * @return {Float} The comparison value
10942      */
10943     asFloat : function(s) {
10944         var val = parseFloat(String(s).replace(/,/g, ""));
10945         if(isNaN(val)) {
10946             val = 0;
10947         }
10948         return val;
10949     },
10950     
10951     /**
10952      * Integer sorting
10953      * @param {Mixed} s The value being converted
10954      * @return {Number} The comparison value
10955      */
10956     asInt : function(s) {
10957         var val = parseInt(String(s).replace(/,/g, ""));
10958         if(isNaN(val)) {
10959             val = 0;
10960         }
10961         return val;
10962     }
10963 };/*
10964  * Based on:
10965  * Ext JS Library 1.1.1
10966  * Copyright(c) 2006-2007, Ext JS, LLC.
10967  *
10968  * Originally Released Under LGPL - original licence link has changed is not relivant.
10969  *
10970  * Fork - LGPL
10971  * <script type="text/javascript">
10972  */
10973
10974 /**
10975 * @class Roo.data.Record
10976  * Instances of this class encapsulate both record <em>definition</em> information, and record
10977  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10978  * to access Records cached in an {@link Roo.data.Store} object.<br>
10979  * <p>
10980  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10981  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10982  * objects.<br>
10983  * <p>
10984  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10985  * @constructor
10986  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10987  * {@link #create}. The parameters are the same.
10988  * @param {Array} data An associative Array of data values keyed by the field name.
10989  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10990  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10991  * not specified an integer id is generated.
10992  */
10993 Roo.data.Record = function(data, id){
10994     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10995     this.data = data;
10996 };
10997
10998 /**
10999  * Generate a constructor for a specific record layout.
11000  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11001  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11002  * Each field definition object may contain the following properties: <ul>
11003  * <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,
11004  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11005  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11006  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11007  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11008  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11009  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11010  * this may be omitted.</p></li>
11011  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11012  * <ul><li>auto (Default, implies no conversion)</li>
11013  * <li>string</li>
11014  * <li>int</li>
11015  * <li>float</li>
11016  * <li>boolean</li>
11017  * <li>date</li></ul></p></li>
11018  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11019  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11020  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11021  * by the Reader into an object that will be stored in the Record. It is passed the
11022  * following parameters:<ul>
11023  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11024  * </ul></p></li>
11025  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11026  * </ul>
11027  * <br>usage:<br><pre><code>
11028 var TopicRecord = Roo.data.Record.create(
11029     {name: 'title', mapping: 'topic_title'},
11030     {name: 'author', mapping: 'username'},
11031     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11032     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11033     {name: 'lastPoster', mapping: 'user2'},
11034     {name: 'excerpt', mapping: 'post_text'}
11035 );
11036
11037 var myNewRecord = new TopicRecord({
11038     title: 'Do my job please',
11039     author: 'noobie',
11040     totalPosts: 1,
11041     lastPost: new Date(),
11042     lastPoster: 'Animal',
11043     excerpt: 'No way dude!'
11044 });
11045 myStore.add(myNewRecord);
11046 </code></pre>
11047  * @method create
11048  * @static
11049  */
11050 Roo.data.Record.create = function(o){
11051     var f = function(){
11052         f.superclass.constructor.apply(this, arguments);
11053     };
11054     Roo.extend(f, Roo.data.Record);
11055     var p = f.prototype;
11056     p.fields = new Roo.util.MixedCollection(false, function(field){
11057         return field.name;
11058     });
11059     for(var i = 0, len = o.length; i < len; i++){
11060         p.fields.add(new Roo.data.Field(o[i]));
11061     }
11062     f.getField = function(name){
11063         return p.fields.get(name);  
11064     };
11065     return f;
11066 };
11067
11068 Roo.data.Record.AUTO_ID = 1000;
11069 Roo.data.Record.EDIT = 'edit';
11070 Roo.data.Record.REJECT = 'reject';
11071 Roo.data.Record.COMMIT = 'commit';
11072
11073 Roo.data.Record.prototype = {
11074     /**
11075      * Readonly flag - true if this record has been modified.
11076      * @type Boolean
11077      */
11078     dirty : false,
11079     editing : false,
11080     error: null,
11081     modified: null,
11082
11083     // private
11084     join : function(store){
11085         this.store = store;
11086     },
11087
11088     /**
11089      * Set the named field to the specified value.
11090      * @param {String} name The name of the field to set.
11091      * @param {Object} value The value to set the field to.
11092      */
11093     set : function(name, value){
11094         if(this.data[name] == value){
11095             return;
11096         }
11097         this.dirty = true;
11098         if(!this.modified){
11099             this.modified = {};
11100         }
11101         if(typeof this.modified[name] == 'undefined'){
11102             this.modified[name] = this.data[name];
11103         }
11104         this.data[name] = value;
11105         if(!this.editing && this.store){
11106             this.store.afterEdit(this);
11107         }       
11108     },
11109
11110     /**
11111      * Get the value of the named field.
11112      * @param {String} name The name of the field to get the value of.
11113      * @return {Object} The value of the field.
11114      */
11115     get : function(name){
11116         return this.data[name]; 
11117     },
11118
11119     // private
11120     beginEdit : function(){
11121         this.editing = true;
11122         this.modified = {}; 
11123     },
11124
11125     // private
11126     cancelEdit : function(){
11127         this.editing = false;
11128         delete this.modified;
11129     },
11130
11131     // private
11132     endEdit : function(){
11133         this.editing = false;
11134         if(this.dirty && this.store){
11135             this.store.afterEdit(this);
11136         }
11137     },
11138
11139     /**
11140      * Usually called by the {@link Roo.data.Store} which owns the Record.
11141      * Rejects all changes made to the Record since either creation, or the last commit operation.
11142      * Modified fields are reverted to their original values.
11143      * <p>
11144      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11145      * of reject operations.
11146      */
11147     reject : function(){
11148         var m = this.modified;
11149         for(var n in m){
11150             if(typeof m[n] != "function"){
11151                 this.data[n] = m[n];
11152             }
11153         }
11154         this.dirty = false;
11155         delete this.modified;
11156         this.editing = false;
11157         if(this.store){
11158             this.store.afterReject(this);
11159         }
11160     },
11161
11162     /**
11163      * Usually called by the {@link Roo.data.Store} which owns the Record.
11164      * Commits all changes made to the Record since either creation, or the last commit operation.
11165      * <p>
11166      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11167      * of commit operations.
11168      */
11169     commit : function(){
11170         this.dirty = false;
11171         delete this.modified;
11172         this.editing = false;
11173         if(this.store){
11174             this.store.afterCommit(this);
11175         }
11176     },
11177
11178     // private
11179     hasError : function(){
11180         return this.error != null;
11181     },
11182
11183     // private
11184     clearError : function(){
11185         this.error = null;
11186     },
11187
11188     /**
11189      * Creates a copy of this record.
11190      * @param {String} id (optional) A new record id if you don't want to use this record's id
11191      * @return {Record}
11192      */
11193     copy : function(newId) {
11194         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11195     }
11196 };/*
11197  * Based on:
11198  * Ext JS Library 1.1.1
11199  * Copyright(c) 2006-2007, Ext JS, LLC.
11200  *
11201  * Originally Released Under LGPL - original licence link has changed is not relivant.
11202  *
11203  * Fork - LGPL
11204  * <script type="text/javascript">
11205  */
11206
11207
11208
11209 /**
11210  * @class Roo.data.Store
11211  * @extends Roo.util.Observable
11212  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11213  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11214  * <p>
11215  * 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
11216  * has no knowledge of the format of the data returned by the Proxy.<br>
11217  * <p>
11218  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11219  * instances from the data object. These records are cached and made available through accessor functions.
11220  * @constructor
11221  * Creates a new Store.
11222  * @param {Object} config A config object containing the objects needed for the Store to access data,
11223  * and read the data into Records.
11224  */
11225 Roo.data.Store = function(config){
11226     this.data = new Roo.util.MixedCollection(false);
11227     this.data.getKey = function(o){
11228         return o.id;
11229     };
11230     this.baseParams = {};
11231     // private
11232     this.paramNames = {
11233         "start" : "start",
11234         "limit" : "limit",
11235         "sort" : "sort",
11236         "dir" : "dir",
11237         "multisort" : "_multisort"
11238     };
11239
11240     if(config && config.data){
11241         this.inlineData = config.data;
11242         delete config.data;
11243     }
11244
11245     Roo.apply(this, config);
11246     
11247     if(this.reader){ // reader passed
11248         this.reader = Roo.factory(this.reader, Roo.data);
11249         this.reader.xmodule = this.xmodule || false;
11250         if(!this.recordType){
11251             this.recordType = this.reader.recordType;
11252         }
11253         if(this.reader.onMetaChange){
11254             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11255         }
11256     }
11257
11258     if(this.recordType){
11259         this.fields = this.recordType.prototype.fields;
11260     }
11261     this.modified = [];
11262
11263     this.addEvents({
11264         /**
11265          * @event datachanged
11266          * Fires when the data cache has changed, and a widget which is using this Store
11267          * as a Record cache should refresh its view.
11268          * @param {Store} this
11269          */
11270         datachanged : true,
11271         /**
11272          * @event metachange
11273          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11274          * @param {Store} this
11275          * @param {Object} meta The JSON metadata
11276          */
11277         metachange : true,
11278         /**
11279          * @event add
11280          * Fires when Records have been added to the Store
11281          * @param {Store} this
11282          * @param {Roo.data.Record[]} records The array of Records added
11283          * @param {Number} index The index at which the record(s) were added
11284          */
11285         add : true,
11286         /**
11287          * @event remove
11288          * Fires when a Record has been removed from the Store
11289          * @param {Store} this
11290          * @param {Roo.data.Record} record The Record that was removed
11291          * @param {Number} index The index at which the record was removed
11292          */
11293         remove : true,
11294         /**
11295          * @event update
11296          * Fires when a Record has been updated
11297          * @param {Store} this
11298          * @param {Roo.data.Record} record The Record that was updated
11299          * @param {String} operation The update operation being performed.  Value may be one of:
11300          * <pre><code>
11301  Roo.data.Record.EDIT
11302  Roo.data.Record.REJECT
11303  Roo.data.Record.COMMIT
11304          * </code></pre>
11305          */
11306         update : true,
11307         /**
11308          * @event clear
11309          * Fires when the data cache has been cleared.
11310          * @param {Store} this
11311          */
11312         clear : true,
11313         /**
11314          * @event beforeload
11315          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11316          * the load action will be canceled.
11317          * @param {Store} this
11318          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11319          */
11320         beforeload : true,
11321         /**
11322          * @event beforeloadadd
11323          * Fires after a new set of Records has been loaded.
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          */
11328         beforeloadadd : true,
11329         /**
11330          * @event load
11331          * Fires after a new set of Records has been loaded, before they are added to the store.
11332          * @param {Store} this
11333          * @param {Roo.data.Record[]} records The Records that were loaded
11334          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11335          * @params {Object} return from reader
11336          */
11337         load : true,
11338         /**
11339          * @event loadexception
11340          * Fires if an exception occurs in the Proxy during loading.
11341          * Called with the signature of the Proxy's "loadexception" event.
11342          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11343          * 
11344          * @param {Proxy} 
11345          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11346          * @param {Object} load options 
11347          * @param {Object} jsonData from your request (normally this contains the Exception)
11348          */
11349         loadexception : true
11350     });
11351     
11352     if(this.proxy){
11353         this.proxy = Roo.factory(this.proxy, Roo.data);
11354         this.proxy.xmodule = this.xmodule || false;
11355         this.relayEvents(this.proxy,  ["loadexception"]);
11356     }
11357     this.sortToggle = {};
11358     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11359
11360     Roo.data.Store.superclass.constructor.call(this);
11361
11362     if(this.inlineData){
11363         this.loadData(this.inlineData);
11364         delete this.inlineData;
11365     }
11366 };
11367
11368 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11369      /**
11370     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11371     * without a remote query - used by combo/forms at present.
11372     */
11373     
11374     /**
11375     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11376     */
11377     /**
11378     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11379     */
11380     /**
11381     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11382     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11383     */
11384     /**
11385     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11386     * on any HTTP request
11387     */
11388     /**
11389     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11390     */
11391     /**
11392     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11393     */
11394     multiSort: false,
11395     /**
11396     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11397     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11398     */
11399     remoteSort : false,
11400
11401     /**
11402     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11403      * loaded or when a record is removed. (defaults to false).
11404     */
11405     pruneModifiedRecords : false,
11406
11407     // private
11408     lastOptions : null,
11409
11410     /**
11411      * Add Records to the Store and fires the add event.
11412      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11413      */
11414     add : function(records){
11415         records = [].concat(records);
11416         for(var i = 0, len = records.length; i < len; i++){
11417             records[i].join(this);
11418         }
11419         var index = this.data.length;
11420         this.data.addAll(records);
11421         this.fireEvent("add", this, records, index);
11422     },
11423
11424     /**
11425      * Remove a Record from the Store and fires the remove event.
11426      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11427      */
11428     remove : function(record){
11429         var index = this.data.indexOf(record);
11430         this.data.removeAt(index);
11431  
11432         if(this.pruneModifiedRecords){
11433             this.modified.remove(record);
11434         }
11435         this.fireEvent("remove", this, record, index);
11436     },
11437
11438     /**
11439      * Remove all Records from the Store and fires the clear event.
11440      */
11441     removeAll : function(){
11442         this.data.clear();
11443         if(this.pruneModifiedRecords){
11444             this.modified = [];
11445         }
11446         this.fireEvent("clear", this);
11447     },
11448
11449     /**
11450      * Inserts Records to the Store at the given index and fires the add event.
11451      * @param {Number} index The start index at which to insert the passed Records.
11452      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11453      */
11454     insert : function(index, records){
11455         records = [].concat(records);
11456         for(var i = 0, len = records.length; i < len; i++){
11457             this.data.insert(index, records[i]);
11458             records[i].join(this);
11459         }
11460         this.fireEvent("add", this, records, index);
11461     },
11462
11463     /**
11464      * Get the index within the cache of the passed Record.
11465      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11466      * @return {Number} The index of the passed Record. Returns -1 if not found.
11467      */
11468     indexOf : function(record){
11469         return this.data.indexOf(record);
11470     },
11471
11472     /**
11473      * Get the index within the cache of the Record with the passed id.
11474      * @param {String} id The id of the Record to find.
11475      * @return {Number} The index of the Record. Returns -1 if not found.
11476      */
11477     indexOfId : function(id){
11478         return this.data.indexOfKey(id);
11479     },
11480
11481     /**
11482      * Get the Record with the specified id.
11483      * @param {String} id The id of the Record to find.
11484      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11485      */
11486     getById : function(id){
11487         return this.data.key(id);
11488     },
11489
11490     /**
11491      * Get the Record at the specified index.
11492      * @param {Number} index The index of the Record to find.
11493      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11494      */
11495     getAt : function(index){
11496         return this.data.itemAt(index);
11497     },
11498
11499     /**
11500      * Returns a range of Records between specified indices.
11501      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11502      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11503      * @return {Roo.data.Record[]} An array of Records
11504      */
11505     getRange : function(start, end){
11506         return this.data.getRange(start, end);
11507     },
11508
11509     // private
11510     storeOptions : function(o){
11511         o = Roo.apply({}, o);
11512         delete o.callback;
11513         delete o.scope;
11514         this.lastOptions = o;
11515     },
11516
11517     /**
11518      * Loads the Record cache from the configured Proxy using the configured Reader.
11519      * <p>
11520      * If using remote paging, then the first load call must specify the <em>start</em>
11521      * and <em>limit</em> properties in the options.params property to establish the initial
11522      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11523      * <p>
11524      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11525      * and this call will return before the new data has been loaded. Perform any post-processing
11526      * in a callback function, or in a "load" event handler.</strong>
11527      * <p>
11528      * @param {Object} options An object containing properties which control loading options:<ul>
11529      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11530      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11531      * passed the following arguments:<ul>
11532      * <li>r : Roo.data.Record[]</li>
11533      * <li>options: Options object from the load call</li>
11534      * <li>success: Boolean success indicator</li></ul></li>
11535      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11536      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11537      * </ul>
11538      */
11539     load : function(options){
11540         options = options || {};
11541         if(this.fireEvent("beforeload", this, options) !== false){
11542             this.storeOptions(options);
11543             var p = Roo.apply(options.params || {}, this.baseParams);
11544             // if meta was not loaded from remote source.. try requesting it.
11545             if (!this.reader.metaFromRemote) {
11546                 p._requestMeta = 1;
11547             }
11548             if(this.sortInfo && this.remoteSort){
11549                 var pn = this.paramNames;
11550                 p[pn["sort"]] = this.sortInfo.field;
11551                 p[pn["dir"]] = this.sortInfo.direction;
11552             }
11553             if (this.multiSort) {
11554                 var pn = this.paramNames;
11555                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11556             }
11557             
11558             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11559         }
11560     },
11561
11562     /**
11563      * Reloads the Record cache from the configured Proxy using the configured Reader and
11564      * the options from the last load operation performed.
11565      * @param {Object} options (optional) An object containing properties which may override the options
11566      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11567      * the most recently used options are reused).
11568      */
11569     reload : function(options){
11570         this.load(Roo.applyIf(options||{}, this.lastOptions));
11571     },
11572
11573     // private
11574     // Called as a callback by the Reader during a load operation.
11575     loadRecords : function(o, options, success){
11576         if(!o || success === false){
11577             if(success !== false){
11578                 this.fireEvent("load", this, [], options, o);
11579             }
11580             if(options.callback){
11581                 options.callback.call(options.scope || this, [], options, false);
11582             }
11583             return;
11584         }
11585         // if data returned failure - throw an exception.
11586         if (o.success === false) {
11587             // show a message if no listener is registered.
11588             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11589                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11590             }
11591             // loadmask wil be hooked into this..
11592             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11593             return;
11594         }
11595         var r = o.records, t = o.totalRecords || r.length;
11596         
11597         this.fireEvent("beforeloadadd", this, r, options, o);
11598         
11599         if(!options || options.add !== true){
11600             if(this.pruneModifiedRecords){
11601                 this.modified = [];
11602             }
11603             for(var i = 0, len = r.length; i < len; i++){
11604                 r[i].join(this);
11605             }
11606             if(this.snapshot){
11607                 this.data = this.snapshot;
11608                 delete this.snapshot;
11609             }
11610             this.data.clear();
11611             this.data.addAll(r);
11612             this.totalLength = t;
11613             this.applySort();
11614             this.fireEvent("datachanged", this);
11615         }else{
11616             this.totalLength = Math.max(t, this.data.length+r.length);
11617             this.add(r);
11618         }
11619         
11620         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11621                 
11622             var e = new Roo.data.Record({});
11623
11624             e.set(this.parent.displayField, this.parent.emptyTitle);
11625             e.set(this.parent.valueField, '');
11626
11627             this.insert(0, e);
11628         }
11629             
11630         this.fireEvent("load", this, r, options, o);
11631         if(options.callback){
11632             options.callback.call(options.scope || this, r, options, true);
11633         }
11634     },
11635
11636
11637     /**
11638      * Loads data from a passed data block. A Reader which understands the format of the data
11639      * must have been configured in the constructor.
11640      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11641      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11642      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11643      */
11644     loadData : function(o, append){
11645         var r = this.reader.readRecords(o);
11646         this.loadRecords(r, {add: append}, true);
11647     },
11648
11649     /**
11650      * Gets the number of cached records.
11651      * <p>
11652      * <em>If using paging, this may not be the total size of the dataset. If the data object
11653      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11654      * the data set size</em>
11655      */
11656     getCount : function(){
11657         return this.data.length || 0;
11658     },
11659
11660     /**
11661      * Gets the total number of records in the dataset as returned by the server.
11662      * <p>
11663      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11664      * the dataset size</em>
11665      */
11666     getTotalCount : function(){
11667         return this.totalLength || 0;
11668     },
11669
11670     /**
11671      * Returns the sort state of the Store as an object with two properties:
11672      * <pre><code>
11673  field {String} The name of the field by which the Records are sorted
11674  direction {String} The sort order, "ASC" or "DESC"
11675      * </code></pre>
11676      */
11677     getSortState : function(){
11678         return this.sortInfo;
11679     },
11680
11681     // private
11682     applySort : function(){
11683         if(this.sortInfo && !this.remoteSort){
11684             var s = this.sortInfo, f = s.field;
11685             var st = this.fields.get(f).sortType;
11686             var fn = function(r1, r2){
11687                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11688                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11689             };
11690             this.data.sort(s.direction, fn);
11691             if(this.snapshot && this.snapshot != this.data){
11692                 this.snapshot.sort(s.direction, fn);
11693             }
11694         }
11695     },
11696
11697     /**
11698      * Sets the default sort column and order to be used by the next load operation.
11699      * @param {String} fieldName The name of the field to sort by.
11700      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11701      */
11702     setDefaultSort : function(field, dir){
11703         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11704     },
11705
11706     /**
11707      * Sort the Records.
11708      * If remote sorting is used, the sort is performed on the server, and the cache is
11709      * reloaded. If local sorting is used, the cache is sorted internally.
11710      * @param {String} fieldName The name of the field to sort by.
11711      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11712      */
11713     sort : function(fieldName, dir){
11714         var f = this.fields.get(fieldName);
11715         if(!dir){
11716             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11717             
11718             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11719                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11720             }else{
11721                 dir = f.sortDir;
11722             }
11723         }
11724         this.sortToggle[f.name] = dir;
11725         this.sortInfo = {field: f.name, direction: dir};
11726         if(!this.remoteSort){
11727             this.applySort();
11728             this.fireEvent("datachanged", this);
11729         }else{
11730             this.load(this.lastOptions);
11731         }
11732     },
11733
11734     /**
11735      * Calls the specified function for each of the Records in the cache.
11736      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11737      * Returning <em>false</em> aborts and exits the iteration.
11738      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11739      */
11740     each : function(fn, scope){
11741         this.data.each(fn, scope);
11742     },
11743
11744     /**
11745      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11746      * (e.g., during paging).
11747      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11748      */
11749     getModifiedRecords : function(){
11750         return this.modified;
11751     },
11752
11753     // private
11754     createFilterFn : function(property, value, anyMatch){
11755         if(!value.exec){ // not a regex
11756             value = String(value);
11757             if(value.length == 0){
11758                 return false;
11759             }
11760             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11761         }
11762         return function(r){
11763             return value.test(r.data[property]);
11764         };
11765     },
11766
11767     /**
11768      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11769      * @param {String} property A field on your records
11770      * @param {Number} start The record index to start at (defaults to 0)
11771      * @param {Number} end The last record index to include (defaults to length - 1)
11772      * @return {Number} The sum
11773      */
11774     sum : function(property, start, end){
11775         var rs = this.data.items, v = 0;
11776         start = start || 0;
11777         end = (end || end === 0) ? end : rs.length-1;
11778
11779         for(var i = start; i <= end; i++){
11780             v += (rs[i].data[property] || 0);
11781         }
11782         return v;
11783     },
11784
11785     /**
11786      * Filter the records by a specified property.
11787      * @param {String} field A field on your records
11788      * @param {String/RegExp} value Either a string that the field
11789      * should start with or a RegExp to test against the field
11790      * @param {Boolean} anyMatch True to match any part not just the beginning
11791      */
11792     filter : function(property, value, anyMatch){
11793         var fn = this.createFilterFn(property, value, anyMatch);
11794         return fn ? this.filterBy(fn) : this.clearFilter();
11795     },
11796
11797     /**
11798      * Filter by a function. The specified function will be called with each
11799      * record in this data source. If the function returns true the record is included,
11800      * otherwise it is filtered.
11801      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11802      * @param {Object} scope (optional) The scope of the function (defaults to this)
11803      */
11804     filterBy : function(fn, scope){
11805         this.snapshot = this.snapshot || this.data;
11806         this.data = this.queryBy(fn, scope||this);
11807         this.fireEvent("datachanged", this);
11808     },
11809
11810     /**
11811      * Query the records by a specified property.
11812      * @param {String} field A field on your records
11813      * @param {String/RegExp} value Either a string that the field
11814      * should start with or a RegExp to test against the field
11815      * @param {Boolean} anyMatch True to match any part not just the beginning
11816      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11817      */
11818     query : function(property, value, anyMatch){
11819         var fn = this.createFilterFn(property, value, anyMatch);
11820         return fn ? this.queryBy(fn) : this.data.clone();
11821     },
11822
11823     /**
11824      * Query by a function. The specified function will be called with each
11825      * record in this data source. If the function returns true the record is included
11826      * in the results.
11827      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11828      * @param {Object} scope (optional) The scope of the function (defaults to this)
11829       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11830      **/
11831     queryBy : function(fn, scope){
11832         var data = this.snapshot || this.data;
11833         return data.filterBy(fn, scope||this);
11834     },
11835
11836     /**
11837      * Collects unique values for a particular dataIndex from this store.
11838      * @param {String} dataIndex The property to collect
11839      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11840      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11841      * @return {Array} An array of the unique values
11842      **/
11843     collect : function(dataIndex, allowNull, bypassFilter){
11844         var d = (bypassFilter === true && this.snapshot) ?
11845                 this.snapshot.items : this.data.items;
11846         var v, sv, r = [], l = {};
11847         for(var i = 0, len = d.length; i < len; i++){
11848             v = d[i].data[dataIndex];
11849             sv = String(v);
11850             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11851                 l[sv] = true;
11852                 r[r.length] = v;
11853             }
11854         }
11855         return r;
11856     },
11857
11858     /**
11859      * Revert to a view of the Record cache with no filtering applied.
11860      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11861      */
11862     clearFilter : function(suppressEvent){
11863         if(this.snapshot && this.snapshot != this.data){
11864             this.data = this.snapshot;
11865             delete this.snapshot;
11866             if(suppressEvent !== true){
11867                 this.fireEvent("datachanged", this);
11868             }
11869         }
11870     },
11871
11872     // private
11873     afterEdit : function(record){
11874         if(this.modified.indexOf(record) == -1){
11875             this.modified.push(record);
11876         }
11877         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11878     },
11879     
11880     // private
11881     afterReject : function(record){
11882         this.modified.remove(record);
11883         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11884     },
11885
11886     // private
11887     afterCommit : function(record){
11888         this.modified.remove(record);
11889         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11890     },
11891
11892     /**
11893      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11894      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11895      */
11896     commitChanges : function(){
11897         var m = this.modified.slice(0);
11898         this.modified = [];
11899         for(var i = 0, len = m.length; i < len; i++){
11900             m[i].commit();
11901         }
11902     },
11903
11904     /**
11905      * Cancel outstanding changes on all changed records.
11906      */
11907     rejectChanges : function(){
11908         var m = this.modified.slice(0);
11909         this.modified = [];
11910         for(var i = 0, len = m.length; i < len; i++){
11911             m[i].reject();
11912         }
11913     },
11914
11915     onMetaChange : function(meta, rtype, o){
11916         this.recordType = rtype;
11917         this.fields = rtype.prototype.fields;
11918         delete this.snapshot;
11919         this.sortInfo = meta.sortInfo || this.sortInfo;
11920         this.modified = [];
11921         this.fireEvent('metachange', this, this.reader.meta);
11922     },
11923     
11924     moveIndex : function(data, type)
11925     {
11926         var index = this.indexOf(data);
11927         
11928         var newIndex = index + type;
11929         
11930         this.remove(data);
11931         
11932         this.insert(newIndex, data);
11933         
11934     }
11935 });/*
11936  * Based on:
11937  * Ext JS Library 1.1.1
11938  * Copyright(c) 2006-2007, Ext JS, LLC.
11939  *
11940  * Originally Released Under LGPL - original licence link has changed is not relivant.
11941  *
11942  * Fork - LGPL
11943  * <script type="text/javascript">
11944  */
11945
11946 /**
11947  * @class Roo.data.SimpleStore
11948  * @extends Roo.data.Store
11949  * Small helper class to make creating Stores from Array data easier.
11950  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11951  * @cfg {Array} fields An array of field definition objects, or field name strings.
11952  * @cfg {Array} data The multi-dimensional array of data
11953  * @constructor
11954  * @param {Object} config
11955  */
11956 Roo.data.SimpleStore = function(config){
11957     Roo.data.SimpleStore.superclass.constructor.call(this, {
11958         isLocal : true,
11959         reader: new Roo.data.ArrayReader({
11960                 id: config.id
11961             },
11962             Roo.data.Record.create(config.fields)
11963         ),
11964         proxy : new Roo.data.MemoryProxy(config.data)
11965     });
11966     this.load();
11967 };
11968 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11969  * Based on:
11970  * Ext JS Library 1.1.1
11971  * Copyright(c) 2006-2007, Ext JS, LLC.
11972  *
11973  * Originally Released Under LGPL - original licence link has changed is not relivant.
11974  *
11975  * Fork - LGPL
11976  * <script type="text/javascript">
11977  */
11978
11979 /**
11980 /**
11981  * @extends Roo.data.Store
11982  * @class Roo.data.JsonStore
11983  * Small helper class to make creating Stores for JSON data easier. <br/>
11984 <pre><code>
11985 var store = new Roo.data.JsonStore({
11986     url: 'get-images.php',
11987     root: 'images',
11988     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11989 });
11990 </code></pre>
11991  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11992  * JsonReader and HttpProxy (unless inline data is provided).</b>
11993  * @cfg {Array} fields An array of field definition objects, or field name strings.
11994  * @constructor
11995  * @param {Object} config
11996  */
11997 Roo.data.JsonStore = function(c){
11998     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11999         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12000         reader: new Roo.data.JsonReader(c, c.fields)
12001     }));
12002 };
12003 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12004  * Based on:
12005  * Ext JS Library 1.1.1
12006  * Copyright(c) 2006-2007, Ext JS, LLC.
12007  *
12008  * Originally Released Under LGPL - original licence link has changed is not relivant.
12009  *
12010  * Fork - LGPL
12011  * <script type="text/javascript">
12012  */
12013
12014  
12015 Roo.data.Field = function(config){
12016     if(typeof config == "string"){
12017         config = {name: config};
12018     }
12019     Roo.apply(this, config);
12020     
12021     if(!this.type){
12022         this.type = "auto";
12023     }
12024     
12025     var st = Roo.data.SortTypes;
12026     // named sortTypes are supported, here we look them up
12027     if(typeof this.sortType == "string"){
12028         this.sortType = st[this.sortType];
12029     }
12030     
12031     // set default sortType for strings and dates
12032     if(!this.sortType){
12033         switch(this.type){
12034             case "string":
12035                 this.sortType = st.asUCString;
12036                 break;
12037             case "date":
12038                 this.sortType = st.asDate;
12039                 break;
12040             default:
12041                 this.sortType = st.none;
12042         }
12043     }
12044
12045     // define once
12046     var stripRe = /[\$,%]/g;
12047
12048     // prebuilt conversion function for this field, instead of
12049     // switching every time we're reading a value
12050     if(!this.convert){
12051         var cv, dateFormat = this.dateFormat;
12052         switch(this.type){
12053             case "":
12054             case "auto":
12055             case undefined:
12056                 cv = function(v){ return v; };
12057                 break;
12058             case "string":
12059                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12060                 break;
12061             case "int":
12062                 cv = function(v){
12063                     return v !== undefined && v !== null && v !== '' ?
12064                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12065                     };
12066                 break;
12067             case "float":
12068                 cv = function(v){
12069                     return v !== undefined && v !== null && v !== '' ?
12070                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12071                     };
12072                 break;
12073             case "bool":
12074             case "boolean":
12075                 cv = function(v){ return v === true || v === "true" || v == 1; };
12076                 break;
12077             case "date":
12078                 cv = function(v){
12079                     if(!v){
12080                         return '';
12081                     }
12082                     if(v instanceof Date){
12083                         return v;
12084                     }
12085                     if(dateFormat){
12086                         if(dateFormat == "timestamp"){
12087                             return new Date(v*1000);
12088                         }
12089                         return Date.parseDate(v, dateFormat);
12090                     }
12091                     var parsed = Date.parse(v);
12092                     return parsed ? new Date(parsed) : null;
12093                 };
12094              break;
12095             
12096         }
12097         this.convert = cv;
12098     }
12099 };
12100
12101 Roo.data.Field.prototype = {
12102     dateFormat: null,
12103     defaultValue: "",
12104     mapping: null,
12105     sortType : null,
12106     sortDir : "ASC"
12107 };/*
12108  * Based on:
12109  * Ext JS Library 1.1.1
12110  * Copyright(c) 2006-2007, Ext JS, LLC.
12111  *
12112  * Originally Released Under LGPL - original licence link has changed is not relivant.
12113  *
12114  * Fork - LGPL
12115  * <script type="text/javascript">
12116  */
12117  
12118 // Base class for reading structured data from a data source.  This class is intended to be
12119 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12120
12121 /**
12122  * @class Roo.data.DataReader
12123  * Base class for reading structured data from a data source.  This class is intended to be
12124  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12125  */
12126
12127 Roo.data.DataReader = function(meta, recordType){
12128     
12129     this.meta = meta;
12130     
12131     this.recordType = recordType instanceof Array ? 
12132         Roo.data.Record.create(recordType) : recordType;
12133 };
12134
12135 Roo.data.DataReader.prototype = {
12136      /**
12137      * Create an empty record
12138      * @param {Object} data (optional) - overlay some values
12139      * @return {Roo.data.Record} record created.
12140      */
12141     newRow :  function(d) {
12142         var da =  {};
12143         this.recordType.prototype.fields.each(function(c) {
12144             switch( c.type) {
12145                 case 'int' : da[c.name] = 0; break;
12146                 case 'date' : da[c.name] = new Date(); break;
12147                 case 'float' : da[c.name] = 0.0; break;
12148                 case 'boolean' : da[c.name] = false; break;
12149                 default : da[c.name] = ""; break;
12150             }
12151             
12152         });
12153         return new this.recordType(Roo.apply(da, d));
12154     }
12155     
12156 };/*
12157  * Based on:
12158  * Ext JS Library 1.1.1
12159  * Copyright(c) 2006-2007, Ext JS, LLC.
12160  *
12161  * Originally Released Under LGPL - original licence link has changed is not relivant.
12162  *
12163  * Fork - LGPL
12164  * <script type="text/javascript">
12165  */
12166
12167 /**
12168  * @class Roo.data.DataProxy
12169  * @extends Roo.data.Observable
12170  * This class is an abstract base class for implementations which provide retrieval of
12171  * unformatted data objects.<br>
12172  * <p>
12173  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12174  * (of the appropriate type which knows how to parse the data object) to provide a block of
12175  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12176  * <p>
12177  * Custom implementations must implement the load method as described in
12178  * {@link Roo.data.HttpProxy#load}.
12179  */
12180 Roo.data.DataProxy = function(){
12181     this.addEvents({
12182         /**
12183          * @event beforeload
12184          * Fires before a network request is made to retrieve a data object.
12185          * @param {Object} This DataProxy object.
12186          * @param {Object} params The params parameter to the load function.
12187          */
12188         beforeload : true,
12189         /**
12190          * @event load
12191          * Fires before the load method's callback is called.
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          */
12196         load : true,
12197         /**
12198          * @event loadexception
12199          * Fires if an Exception occurs during data retrieval.
12200          * @param {Object} This DataProxy object.
12201          * @param {Object} o The data object.
12202          * @param {Object} arg The callback argument object passed to the load function.
12203          * @param {Object} e The Exception.
12204          */
12205         loadexception : true
12206     });
12207     Roo.data.DataProxy.superclass.constructor.call(this);
12208 };
12209
12210 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12211
12212     /**
12213      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12214      */
12215 /*
12216  * Based on:
12217  * Ext JS Library 1.1.1
12218  * Copyright(c) 2006-2007, Ext JS, LLC.
12219  *
12220  * Originally Released Under LGPL - original licence link has changed is not relivant.
12221  *
12222  * Fork - LGPL
12223  * <script type="text/javascript">
12224  */
12225 /**
12226  * @class Roo.data.MemoryProxy
12227  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12228  * to the Reader when its load method is called.
12229  * @constructor
12230  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12231  */
12232 Roo.data.MemoryProxy = function(data){
12233     if (data.data) {
12234         data = data.data;
12235     }
12236     Roo.data.MemoryProxy.superclass.constructor.call(this);
12237     this.data = data;
12238 };
12239
12240 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12241     
12242     /**
12243      * Load data from the requested source (in this case an in-memory
12244      * data object passed to the constructor), read the data object into
12245      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12246      * process that block using the passed callback.
12247      * @param {Object} params This parameter is not used by the MemoryProxy class.
12248      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12249      * object into a block of Roo.data.Records.
12250      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12251      * The function must be passed <ul>
12252      * <li>The Record block object</li>
12253      * <li>The "arg" argument from the load function</li>
12254      * <li>A boolean success indicator</li>
12255      * </ul>
12256      * @param {Object} scope The scope in which to call the callback
12257      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12258      */
12259     load : function(params, reader, callback, scope, arg){
12260         params = params || {};
12261         var result;
12262         try {
12263             result = reader.readRecords(this.data);
12264         }catch(e){
12265             this.fireEvent("loadexception", this, arg, null, e);
12266             callback.call(scope, null, arg, false);
12267             return;
12268         }
12269         callback.call(scope, result, arg, true);
12270     },
12271     
12272     // private
12273     update : function(params, records){
12274         
12275     }
12276 });/*
12277  * Based on:
12278  * Ext JS Library 1.1.1
12279  * Copyright(c) 2006-2007, Ext JS, LLC.
12280  *
12281  * Originally Released Under LGPL - original licence link has changed is not relivant.
12282  *
12283  * Fork - LGPL
12284  * <script type="text/javascript">
12285  */
12286 /**
12287  * @class Roo.data.HttpProxy
12288  * @extends Roo.data.DataProxy
12289  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12290  * configured to reference a certain URL.<br><br>
12291  * <p>
12292  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12293  * from which the running page was served.<br><br>
12294  * <p>
12295  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12296  * <p>
12297  * Be aware that to enable the browser to parse an XML document, the server must set
12298  * the Content-Type header in the HTTP response to "text/xml".
12299  * @constructor
12300  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12301  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12302  * will be used to make the request.
12303  */
12304 Roo.data.HttpProxy = function(conn){
12305     Roo.data.HttpProxy.superclass.constructor.call(this);
12306     // is conn a conn config or a real conn?
12307     this.conn = conn;
12308     this.useAjax = !conn || !conn.events;
12309   
12310 };
12311
12312 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12313     // thse are take from connection...
12314     
12315     /**
12316      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12317      */
12318     /**
12319      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12320      * extra parameters to each request made by this object. (defaults to undefined)
12321      */
12322     /**
12323      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12324      *  to each request made by this object. (defaults to undefined)
12325      */
12326     /**
12327      * @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)
12328      */
12329     /**
12330      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12331      */
12332      /**
12333      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12334      * @type Boolean
12335      */
12336   
12337
12338     /**
12339      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12340      * @type Boolean
12341      */
12342     /**
12343      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12344      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12345      * a finer-grained basis than the DataProxy events.
12346      */
12347     getConnection : function(){
12348         return this.useAjax ? Roo.Ajax : this.conn;
12349     },
12350
12351     /**
12352      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12353      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12354      * process that block using the passed callback.
12355      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12356      * for the request to the remote server.
12357      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12358      * object into a block of Roo.data.Records.
12359      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12360      * The function must be passed <ul>
12361      * <li>The Record block object</li>
12362      * <li>The "arg" argument from the load function</li>
12363      * <li>A boolean success indicator</li>
12364      * </ul>
12365      * @param {Object} scope The scope in which to call the callback
12366      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12367      */
12368     load : function(params, reader, callback, scope, arg){
12369         if(this.fireEvent("beforeload", this, params) !== false){
12370             var  o = {
12371                 params : params || {},
12372                 request: {
12373                     callback : callback,
12374                     scope : scope,
12375                     arg : arg
12376                 },
12377                 reader: reader,
12378                 callback : this.loadResponse,
12379                 scope: this
12380             };
12381             if(this.useAjax){
12382                 Roo.applyIf(o, this.conn);
12383                 if(this.activeRequest){
12384                     Roo.Ajax.abort(this.activeRequest);
12385                 }
12386                 this.activeRequest = Roo.Ajax.request(o);
12387             }else{
12388                 this.conn.request(o);
12389             }
12390         }else{
12391             callback.call(scope||this, null, arg, false);
12392         }
12393     },
12394
12395     // private
12396     loadResponse : function(o, success, response){
12397         delete this.activeRequest;
12398         if(!success){
12399             this.fireEvent("loadexception", this, o, response);
12400             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12401             return;
12402         }
12403         var result;
12404         try {
12405             result = o.reader.read(response);
12406         }catch(e){
12407             this.fireEvent("loadexception", this, o, response, e);
12408             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12409             return;
12410         }
12411         
12412         this.fireEvent("load", this, o, o.request.arg);
12413         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12414     },
12415
12416     // private
12417     update : function(dataSet){
12418
12419     },
12420
12421     // private
12422     updateResponse : function(dataSet){
12423
12424     }
12425 });/*
12426  * Based on:
12427  * Ext JS Library 1.1.1
12428  * Copyright(c) 2006-2007, Ext JS, LLC.
12429  *
12430  * Originally Released Under LGPL - original licence link has changed is not relivant.
12431  *
12432  * Fork - LGPL
12433  * <script type="text/javascript">
12434  */
12435
12436 /**
12437  * @class Roo.data.ScriptTagProxy
12438  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12439  * other than the originating domain of the running page.<br><br>
12440  * <p>
12441  * <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
12442  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12443  * <p>
12444  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12445  * source code that is used as the source inside a &lt;script> tag.<br><br>
12446  * <p>
12447  * In order for the browser to process the returned data, the server must wrap the data object
12448  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12449  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12450  * depending on whether the callback name was passed:
12451  * <p>
12452  * <pre><code>
12453 boolean scriptTag = false;
12454 String cb = request.getParameter("callback");
12455 if (cb != null) {
12456     scriptTag = true;
12457     response.setContentType("text/javascript");
12458 } else {
12459     response.setContentType("application/x-json");
12460 }
12461 Writer out = response.getWriter();
12462 if (scriptTag) {
12463     out.write(cb + "(");
12464 }
12465 out.print(dataBlock.toJsonString());
12466 if (scriptTag) {
12467     out.write(");");
12468 }
12469 </pre></code>
12470  *
12471  * @constructor
12472  * @param {Object} config A configuration object.
12473  */
12474 Roo.data.ScriptTagProxy = function(config){
12475     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12476     Roo.apply(this, config);
12477     this.head = document.getElementsByTagName("head")[0];
12478 };
12479
12480 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12481
12482 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12483     /**
12484      * @cfg {String} url The URL from which to request the data object.
12485      */
12486     /**
12487      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12488      */
12489     timeout : 30000,
12490     /**
12491      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12492      * the server the name of the callback function set up by the load call to process the returned data object.
12493      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12494      * javascript output which calls this named function passing the data object as its only parameter.
12495      */
12496     callbackParam : "callback",
12497     /**
12498      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12499      * name to the request.
12500      */
12501     nocache : true,
12502
12503     /**
12504      * Load data from the configured URL, read the data object into
12505      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12506      * process that block using the passed callback.
12507      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12508      * for the request to the remote server.
12509      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12510      * object into a block of Roo.data.Records.
12511      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12512      * The function must be passed <ul>
12513      * <li>The Record block object</li>
12514      * <li>The "arg" argument from the load function</li>
12515      * <li>A boolean success indicator</li>
12516      * </ul>
12517      * @param {Object} scope The scope in which to call the callback
12518      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12519      */
12520     load : function(params, reader, callback, scope, arg){
12521         if(this.fireEvent("beforeload", this, params) !== false){
12522
12523             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12524
12525             var url = this.url;
12526             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12527             if(this.nocache){
12528                 url += "&_dc=" + (new Date().getTime());
12529             }
12530             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12531             var trans = {
12532                 id : transId,
12533                 cb : "stcCallback"+transId,
12534                 scriptId : "stcScript"+transId,
12535                 params : params,
12536                 arg : arg,
12537                 url : url,
12538                 callback : callback,
12539                 scope : scope,
12540                 reader : reader
12541             };
12542             var conn = this;
12543
12544             window[trans.cb] = function(o){
12545                 conn.handleResponse(o, trans);
12546             };
12547
12548             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12549
12550             if(this.autoAbort !== false){
12551                 this.abort();
12552             }
12553
12554             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12555
12556             var script = document.createElement("script");
12557             script.setAttribute("src", url);
12558             script.setAttribute("type", "text/javascript");
12559             script.setAttribute("id", trans.scriptId);
12560             this.head.appendChild(script);
12561
12562             this.trans = trans;
12563         }else{
12564             callback.call(scope||this, null, arg, false);
12565         }
12566     },
12567
12568     // private
12569     isLoading : function(){
12570         return this.trans ? true : false;
12571     },
12572
12573     /**
12574      * Abort the current server request.
12575      */
12576     abort : function(){
12577         if(this.isLoading()){
12578             this.destroyTrans(this.trans);
12579         }
12580     },
12581
12582     // private
12583     destroyTrans : function(trans, isLoaded){
12584         this.head.removeChild(document.getElementById(trans.scriptId));
12585         clearTimeout(trans.timeoutId);
12586         if(isLoaded){
12587             window[trans.cb] = undefined;
12588             try{
12589                 delete window[trans.cb];
12590             }catch(e){}
12591         }else{
12592             // if hasn't been loaded, wait for load to remove it to prevent script error
12593             window[trans.cb] = function(){
12594                 window[trans.cb] = undefined;
12595                 try{
12596                     delete window[trans.cb];
12597                 }catch(e){}
12598             };
12599         }
12600     },
12601
12602     // private
12603     handleResponse : function(o, trans){
12604         this.trans = false;
12605         this.destroyTrans(trans, true);
12606         var result;
12607         try {
12608             result = trans.reader.readRecords(o);
12609         }catch(e){
12610             this.fireEvent("loadexception", this, o, trans.arg, e);
12611             trans.callback.call(trans.scope||window, null, trans.arg, false);
12612             return;
12613         }
12614         this.fireEvent("load", this, o, trans.arg);
12615         trans.callback.call(trans.scope||window, result, trans.arg, true);
12616     },
12617
12618     // private
12619     handleFailure : function(trans){
12620         this.trans = false;
12621         this.destroyTrans(trans, false);
12622         this.fireEvent("loadexception", this, null, trans.arg);
12623         trans.callback.call(trans.scope||window, null, trans.arg, false);
12624     }
12625 });/*
12626  * Based on:
12627  * Ext JS Library 1.1.1
12628  * Copyright(c) 2006-2007, Ext JS, LLC.
12629  *
12630  * Originally Released Under LGPL - original licence link has changed is not relivant.
12631  *
12632  * Fork - LGPL
12633  * <script type="text/javascript">
12634  */
12635
12636 /**
12637  * @class Roo.data.JsonReader
12638  * @extends Roo.data.DataReader
12639  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12640  * based on mappings in a provided Roo.data.Record constructor.
12641  * 
12642  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12643  * in the reply previously. 
12644  * 
12645  * <p>
12646  * Example code:
12647  * <pre><code>
12648 var RecordDef = Roo.data.Record.create([
12649     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12650     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12651 ]);
12652 var myReader = new Roo.data.JsonReader({
12653     totalProperty: "results",    // The property which contains the total dataset size (optional)
12654     root: "rows",                // The property which contains an Array of row objects
12655     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12656 }, RecordDef);
12657 </code></pre>
12658  * <p>
12659  * This would consume a JSON file like this:
12660  * <pre><code>
12661 { 'results': 2, 'rows': [
12662     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12663     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12664 }
12665 </code></pre>
12666  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12667  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12668  * paged from the remote server.
12669  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12670  * @cfg {String} root name of the property which contains the Array of row objects.
12671  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12672  * @cfg {Array} fields Array of field definition objects
12673  * @constructor
12674  * Create a new JsonReader
12675  * @param {Object} meta Metadata configuration options
12676  * @param {Object} recordType Either an Array of field definition objects,
12677  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12678  */
12679 Roo.data.JsonReader = function(meta, recordType){
12680     
12681     meta = meta || {};
12682     // set some defaults:
12683     Roo.applyIf(meta, {
12684         totalProperty: 'total',
12685         successProperty : 'success',
12686         root : 'data',
12687         id : 'id'
12688     });
12689     
12690     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12691 };
12692 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12693     
12694     /**
12695      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12696      * Used by Store query builder to append _requestMeta to params.
12697      * 
12698      */
12699     metaFromRemote : false,
12700     /**
12701      * This method is only used by a DataProxy which has retrieved data from a remote server.
12702      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12703      * @return {Object} data A data block which is used by an Roo.data.Store object as
12704      * a cache of Roo.data.Records.
12705      */
12706     read : function(response){
12707         var json = response.responseText;
12708        
12709         var o = /* eval:var:o */ eval("("+json+")");
12710         if(!o) {
12711             throw {message: "JsonReader.read: Json object not found"};
12712         }
12713         
12714         if(o.metaData){
12715             
12716             delete this.ef;
12717             this.metaFromRemote = true;
12718             this.meta = o.metaData;
12719             this.recordType = Roo.data.Record.create(o.metaData.fields);
12720             this.onMetaChange(this.meta, this.recordType, o);
12721         }
12722         return this.readRecords(o);
12723     },
12724
12725     // private function a store will implement
12726     onMetaChange : function(meta, recordType, o){
12727
12728     },
12729
12730     /**
12731          * @ignore
12732          */
12733     simpleAccess: function(obj, subsc) {
12734         return obj[subsc];
12735     },
12736
12737         /**
12738          * @ignore
12739          */
12740     getJsonAccessor: function(){
12741         var re = /[\[\.]/;
12742         return function(expr) {
12743             try {
12744                 return(re.test(expr))
12745                     ? new Function("obj", "return obj." + expr)
12746                     : function(obj){
12747                         return obj[expr];
12748                     };
12749             } catch(e){}
12750             return Roo.emptyFn;
12751         };
12752     }(),
12753
12754     /**
12755      * Create a data block containing Roo.data.Records from an XML document.
12756      * @param {Object} o An object which contains an Array of row objects in the property specified
12757      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12758      * which contains the total size of the dataset.
12759      * @return {Object} data A data block which is used by an Roo.data.Store object as
12760      * a cache of Roo.data.Records.
12761      */
12762     readRecords : function(o){
12763         /**
12764          * After any data loads, the raw JSON data is available for further custom processing.
12765          * @type Object
12766          */
12767         this.o = o;
12768         var s = this.meta, Record = this.recordType,
12769             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12770
12771 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12772         if (!this.ef) {
12773             if(s.totalProperty) {
12774                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12775                 }
12776                 if(s.successProperty) {
12777                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12778                 }
12779                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12780                 if (s.id) {
12781                         var g = this.getJsonAccessor(s.id);
12782                         this.getId = function(rec) {
12783                                 var r = g(rec);  
12784                                 return (r === undefined || r === "") ? null : r;
12785                         };
12786                 } else {
12787                         this.getId = function(){return null;};
12788                 }
12789             this.ef = [];
12790             for(var jj = 0; jj < fl; jj++){
12791                 f = fi[jj];
12792                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12793                 this.ef[jj] = this.getJsonAccessor(map);
12794             }
12795         }
12796
12797         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12798         if(s.totalProperty){
12799             var vt = parseInt(this.getTotal(o), 10);
12800             if(!isNaN(vt)){
12801                 totalRecords = vt;
12802             }
12803         }
12804         if(s.successProperty){
12805             var vs = this.getSuccess(o);
12806             if(vs === false || vs === 'false'){
12807                 success = false;
12808             }
12809         }
12810         var records = [];
12811         for(var i = 0; i < c; i++){
12812                 var n = root[i];
12813             var values = {};
12814             var id = this.getId(n);
12815             for(var j = 0; j < fl; j++){
12816                 f = fi[j];
12817             var v = this.ef[j](n);
12818             if (!f.convert) {
12819                 Roo.log('missing convert for ' + f.name);
12820                 Roo.log(f);
12821                 continue;
12822             }
12823             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12824             }
12825             var record = new Record(values, id);
12826             record.json = n;
12827             records[i] = record;
12828         }
12829         return {
12830             raw : o,
12831             success : success,
12832             records : records,
12833             totalRecords : totalRecords
12834         };
12835     }
12836 });/*
12837  * Based on:
12838  * Ext JS Library 1.1.1
12839  * Copyright(c) 2006-2007, Ext JS, LLC.
12840  *
12841  * Originally Released Under LGPL - original licence link has changed is not relivant.
12842  *
12843  * Fork - LGPL
12844  * <script type="text/javascript">
12845  */
12846
12847 /**
12848  * @class Roo.data.ArrayReader
12849  * @extends Roo.data.DataReader
12850  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12851  * Each element of that Array represents a row of data fields. The
12852  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12853  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12854  * <p>
12855  * Example code:.
12856  * <pre><code>
12857 var RecordDef = Roo.data.Record.create([
12858     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12859     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12860 ]);
12861 var myReader = new Roo.data.ArrayReader({
12862     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12863 }, RecordDef);
12864 </code></pre>
12865  * <p>
12866  * This would consume an Array like this:
12867  * <pre><code>
12868 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12869   </code></pre>
12870  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12871  * @constructor
12872  * Create a new JsonReader
12873  * @param {Object} meta Metadata configuration options.
12874  * @param {Object} recordType Either an Array of field definition objects
12875  * as specified to {@link Roo.data.Record#create},
12876  * or an {@link Roo.data.Record} object
12877  * created using {@link Roo.data.Record#create}.
12878  */
12879 Roo.data.ArrayReader = function(meta, recordType){
12880     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12881 };
12882
12883 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12884     /**
12885      * Create a data block containing Roo.data.Records from an XML document.
12886      * @param {Object} o An Array of row objects which represents the dataset.
12887      * @return {Object} data A data block which is used by an Roo.data.Store object as
12888      * a cache of Roo.data.Records.
12889      */
12890     readRecords : function(o){
12891         var sid = this.meta ? this.meta.id : null;
12892         var recordType = this.recordType, fields = recordType.prototype.fields;
12893         var records = [];
12894         var root = o;
12895             for(var i = 0; i < root.length; i++){
12896                     var n = root[i];
12897                 var values = {};
12898                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12899                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12900                 var f = fields.items[j];
12901                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12902                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12903                 v = f.convert(v);
12904                 values[f.name] = v;
12905             }
12906                 var record = new recordType(values, id);
12907                 record.json = n;
12908                 records[records.length] = record;
12909             }
12910             return {
12911                 records : records,
12912                 totalRecords : records.length
12913             };
12914     }
12915 });/*
12916  * - LGPL
12917  * * 
12918  */
12919
12920 /**
12921  * @class Roo.bootstrap.ComboBox
12922  * @extends Roo.bootstrap.TriggerField
12923  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12924  * @cfg {Boolean} append (true|false) default false
12925  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12926  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12927  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12928  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12929  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12930  * @cfg {Boolean} animate default true
12931  * @cfg {Boolean} emptyResultText only for touch device
12932  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12933  * @cfg {String} emptyTitle default ''
12934  * @constructor
12935  * Create a new ComboBox.
12936  * @param {Object} config Configuration options
12937  */
12938 Roo.bootstrap.ComboBox = function(config){
12939     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12940     this.addEvents({
12941         /**
12942          * @event expand
12943          * Fires when the dropdown list is expanded
12944         * @param {Roo.bootstrap.ComboBox} combo This combo box
12945         */
12946         'expand' : true,
12947         /**
12948          * @event collapse
12949          * Fires when the dropdown list is collapsed
12950         * @param {Roo.bootstrap.ComboBox} combo This combo box
12951         */
12952         'collapse' : true,
12953         /**
12954          * @event beforeselect
12955          * Fires before a list item is selected. Return false to cancel the selection.
12956         * @param {Roo.bootstrap.ComboBox} combo This combo box
12957         * @param {Roo.data.Record} record The data record returned from the underlying store
12958         * @param {Number} index The index of the selected item in the dropdown list
12959         */
12960         'beforeselect' : true,
12961         /**
12962          * @event select
12963          * Fires when a list item is selected
12964         * @param {Roo.bootstrap.ComboBox} combo This combo box
12965         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12966         * @param {Number} index The index of the selected item in the dropdown list
12967         */
12968         'select' : true,
12969         /**
12970          * @event beforequery
12971          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12972          * The event object passed has these properties:
12973         * @param {Roo.bootstrap.ComboBox} combo This combo box
12974         * @param {String} query The query
12975         * @param {Boolean} forceAll true to force "all" query
12976         * @param {Boolean} cancel true to cancel the query
12977         * @param {Object} e The query event object
12978         */
12979         'beforequery': true,
12980          /**
12981          * @event add
12982          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12983         * @param {Roo.bootstrap.ComboBox} combo This combo box
12984         */
12985         'add' : true,
12986         /**
12987          * @event edit
12988          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12989         * @param {Roo.bootstrap.ComboBox} combo This combo box
12990         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12991         */
12992         'edit' : true,
12993         /**
12994          * @event remove
12995          * Fires when the remove value from the combobox array
12996         * @param {Roo.bootstrap.ComboBox} combo This combo box
12997         */
12998         'remove' : true,
12999         /**
13000          * @event afterremove
13001          * Fires when the remove value from the combobox array
13002         * @param {Roo.bootstrap.ComboBox} combo This combo box
13003         */
13004         'afterremove' : true,
13005         /**
13006          * @event specialfilter
13007          * Fires when specialfilter
13008             * @param {Roo.bootstrap.ComboBox} combo This combo box
13009             */
13010         'specialfilter' : true,
13011         /**
13012          * @event tick
13013          * Fires when tick the element
13014             * @param {Roo.bootstrap.ComboBox} combo This combo box
13015             */
13016         'tick' : true,
13017         /**
13018          * @event touchviewdisplay
13019          * Fires when touch view require special display (default is using displayField)
13020             * @param {Roo.bootstrap.ComboBox} combo This combo box
13021             * @param {Object} cfg set html .
13022             */
13023         'touchviewdisplay' : true
13024         
13025     });
13026     
13027     this.item = [];
13028     this.tickItems = [];
13029     
13030     this.selectedIndex = -1;
13031     if(this.mode == 'local'){
13032         if(config.queryDelay === undefined){
13033             this.queryDelay = 10;
13034         }
13035         if(config.minChars === undefined){
13036             this.minChars = 0;
13037         }
13038     }
13039 };
13040
13041 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13042      
13043     /**
13044      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13045      * rendering into an Roo.Editor, defaults to false)
13046      */
13047     /**
13048      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13049      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13050      */
13051     /**
13052      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13053      */
13054     /**
13055      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13056      * the dropdown list (defaults to undefined, with no header element)
13057      */
13058
13059      /**
13060      * @cfg {String/Roo.Template} tpl The template to use to render the output
13061      */
13062      
13063      /**
13064      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13065      */
13066     listWidth: undefined,
13067     /**
13068      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13069      * mode = 'remote' or 'text' if mode = 'local')
13070      */
13071     displayField: undefined,
13072     
13073     /**
13074      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13075      * mode = 'remote' or 'value' if mode = 'local'). 
13076      * Note: use of a valueField requires the user make a selection
13077      * in order for a value to be mapped.
13078      */
13079     valueField: undefined,
13080     /**
13081      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13082      */
13083     modalTitle : '',
13084     
13085     /**
13086      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13087      * field's data value (defaults to the underlying DOM element's name)
13088      */
13089     hiddenName: undefined,
13090     /**
13091      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13092      */
13093     listClass: '',
13094     /**
13095      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13096      */
13097     selectedClass: 'active',
13098     
13099     /**
13100      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13101      */
13102     shadow:'sides',
13103     /**
13104      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13105      * anchor positions (defaults to 'tl-bl')
13106      */
13107     listAlign: 'tl-bl?',
13108     /**
13109      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13110      */
13111     maxHeight: 300,
13112     /**
13113      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13114      * query specified by the allQuery config option (defaults to 'query')
13115      */
13116     triggerAction: 'query',
13117     /**
13118      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13119      * (defaults to 4, does not apply if editable = false)
13120      */
13121     minChars : 4,
13122     /**
13123      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13124      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13125      */
13126     typeAhead: false,
13127     /**
13128      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13129      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13130      */
13131     queryDelay: 500,
13132     /**
13133      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13134      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13135      */
13136     pageSize: 0,
13137     /**
13138      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13139      * when editable = true (defaults to false)
13140      */
13141     selectOnFocus:false,
13142     /**
13143      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13144      */
13145     queryParam: 'query',
13146     /**
13147      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13148      * when mode = 'remote' (defaults to 'Loading...')
13149      */
13150     loadingText: 'Loading...',
13151     /**
13152      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13153      */
13154     resizable: false,
13155     /**
13156      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13157      */
13158     handleHeight : 8,
13159     /**
13160      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13161      * traditional select (defaults to true)
13162      */
13163     editable: true,
13164     /**
13165      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13166      */
13167     allQuery: '',
13168     /**
13169      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13170      */
13171     mode: 'remote',
13172     /**
13173      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13174      * listWidth has a higher value)
13175      */
13176     minListWidth : 70,
13177     /**
13178      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13179      * allow the user to set arbitrary text into the field (defaults to false)
13180      */
13181     forceSelection:false,
13182     /**
13183      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13184      * if typeAhead = true (defaults to 250)
13185      */
13186     typeAheadDelay : 250,
13187     /**
13188      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13189      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13190      */
13191     valueNotFoundText : undefined,
13192     /**
13193      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13194      */
13195     blockFocus : false,
13196     
13197     /**
13198      * @cfg {Boolean} disableClear Disable showing of clear button.
13199      */
13200     disableClear : false,
13201     /**
13202      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13203      */
13204     alwaysQuery : false,
13205     
13206     /**
13207      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13208      */
13209     multiple : false,
13210     
13211     /**
13212      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13213      */
13214     invalidClass : "has-warning",
13215     
13216     /**
13217      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13218      */
13219     validClass : "has-success",
13220     
13221     /**
13222      * @cfg {Boolean} specialFilter (true|false) special filter default false
13223      */
13224     specialFilter : false,
13225     
13226     /**
13227      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13228      */
13229     mobileTouchView : true,
13230     
13231     /**
13232      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13233      */
13234     useNativeIOS : false,
13235     
13236     /**
13237      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13238      */
13239     mobile_restrict_height : false,
13240     
13241     ios_options : false,
13242     
13243     //private
13244     addicon : false,
13245     editicon: false,
13246     
13247     page: 0,
13248     hasQuery: false,
13249     append: false,
13250     loadNext: false,
13251     autoFocus : true,
13252     tickable : false,
13253     btnPosition : 'right',
13254     triggerList : true,
13255     showToggleBtn : true,
13256     animate : true,
13257     emptyResultText: 'Empty',
13258     triggerText : 'Select',
13259     emptyTitle : '',
13260     
13261     // element that contains real text value.. (when hidden is used..)
13262     
13263     getAutoCreate : function()
13264     {   
13265         var cfg = false;
13266         //render
13267         /*
13268          * Render classic select for iso
13269          */
13270         
13271         if(Roo.isIOS && this.useNativeIOS){
13272             cfg = this.getAutoCreateNativeIOS();
13273             return cfg;
13274         }
13275         
13276         /*
13277          * Touch Devices
13278          */
13279         
13280         if(Roo.isTouch && this.mobileTouchView){
13281             cfg = this.getAutoCreateTouchView();
13282             return cfg;;
13283         }
13284         
13285         /*
13286          *  Normal ComboBox
13287          */
13288         if(!this.tickable){
13289             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13290             return cfg;
13291         }
13292         
13293         /*
13294          *  ComboBox with tickable selections
13295          */
13296              
13297         var align = this.labelAlign || this.parentLabelAlign();
13298         
13299         cfg = {
13300             cls : 'form-group roo-combobox-tickable' //input-group
13301         };
13302         
13303         var btn_text_select = '';
13304         var btn_text_done = '';
13305         var btn_text_cancel = '';
13306         
13307         if (this.btn_text_show) {
13308             btn_text_select = 'Select';
13309             btn_text_done = 'Done';
13310             btn_text_cancel = 'Cancel'; 
13311         }
13312         
13313         var buttons = {
13314             tag : 'div',
13315             cls : 'tickable-buttons',
13316             cn : [
13317                 {
13318                     tag : 'button',
13319                     type : 'button',
13320                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13321                     //html : this.triggerText
13322                     html: btn_text_select
13323                 },
13324                 {
13325                     tag : 'button',
13326                     type : 'button',
13327                     name : 'ok',
13328                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13329                     //html : 'Done'
13330                     html: btn_text_done
13331                 },
13332                 {
13333                     tag : 'button',
13334                     type : 'button',
13335                     name : 'cancel',
13336                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13337                     //html : 'Cancel'
13338                     html: btn_text_cancel
13339                 }
13340             ]
13341         };
13342         
13343         if(this.editable){
13344             buttons.cn.unshift({
13345                 tag: 'input',
13346                 cls: 'roo-select2-search-field-input'
13347             });
13348         }
13349         
13350         var _this = this;
13351         
13352         Roo.each(buttons.cn, function(c){
13353             if (_this.size) {
13354                 c.cls += ' btn-' + _this.size;
13355             }
13356
13357             if (_this.disabled) {
13358                 c.disabled = true;
13359             }
13360         });
13361         
13362         var box = {
13363             tag: 'div',
13364             cn: [
13365                 {
13366                     tag: 'input',
13367                     type : 'hidden',
13368                     cls: 'form-hidden-field'
13369                 },
13370                 {
13371                     tag: 'ul',
13372                     cls: 'roo-select2-choices',
13373                     cn:[
13374                         {
13375                             tag: 'li',
13376                             cls: 'roo-select2-search-field',
13377                             cn: [
13378                                 buttons
13379                             ]
13380                         }
13381                     ]
13382                 }
13383             ]
13384         };
13385         
13386         var combobox = {
13387             cls: 'roo-select2-container input-group roo-select2-container-multi',
13388             cn: [
13389                 
13390                 box
13391 //                {
13392 //                    tag: 'ul',
13393 //                    cls: 'typeahead typeahead-long dropdown-menu',
13394 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13395 //                }
13396             ]
13397         };
13398         
13399         if(this.hasFeedback && !this.allowBlank){
13400             
13401             var feedback = {
13402                 tag: 'span',
13403                 cls: 'glyphicon form-control-feedback'
13404             };
13405
13406             combobox.cn.push(feedback);
13407         }
13408         
13409         var indicator = {
13410             tag : 'i',
13411             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13412             tooltip : 'This field is required'
13413         };
13414         if (Roo.bootstrap.version == 4) {
13415             indicator = {
13416                 tag : 'i',
13417                 style : 'display:none'
13418             };
13419         }
13420         if (align ==='left' && this.fieldLabel.length) {
13421             
13422             cfg.cls += ' roo-form-group-label-left row';
13423             
13424             cfg.cn = [
13425                 indicator,
13426                 {
13427                     tag: 'label',
13428                     'for' :  id,
13429                     cls : 'control-label col-form-label',
13430                     html : this.fieldLabel
13431
13432                 },
13433                 {
13434                     cls : "", 
13435                     cn: [
13436                         combobox
13437                     ]
13438                 }
13439
13440             ];
13441             
13442             var labelCfg = cfg.cn[1];
13443             var contentCfg = cfg.cn[2];
13444             
13445
13446             if(this.indicatorpos == 'right'){
13447                 
13448                 cfg.cn = [
13449                     {
13450                         tag: 'label',
13451                         'for' :  id,
13452                         cls : 'control-label col-form-label',
13453                         cn : [
13454                             {
13455                                 tag : 'span',
13456                                 html : this.fieldLabel
13457                             },
13458                             indicator
13459                         ]
13460                     },
13461                     {
13462                         cls : "",
13463                         cn: [
13464                             combobox
13465                         ]
13466                     }
13467
13468                 ];
13469                 
13470                 
13471                 
13472                 labelCfg = cfg.cn[0];
13473                 contentCfg = cfg.cn[1];
13474             
13475             }
13476             
13477             if(this.labelWidth > 12){
13478                 labelCfg.style = "width: " + this.labelWidth + 'px';
13479             }
13480             
13481             if(this.labelWidth < 13 && this.labelmd == 0){
13482                 this.labelmd = this.labelWidth;
13483             }
13484             
13485             if(this.labellg > 0){
13486                 labelCfg.cls += ' col-lg-' + this.labellg;
13487                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13488             }
13489             
13490             if(this.labelmd > 0){
13491                 labelCfg.cls += ' col-md-' + this.labelmd;
13492                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13493             }
13494             
13495             if(this.labelsm > 0){
13496                 labelCfg.cls += ' col-sm-' + this.labelsm;
13497                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13498             }
13499             
13500             if(this.labelxs > 0){
13501                 labelCfg.cls += ' col-xs-' + this.labelxs;
13502                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13503             }
13504                 
13505                 
13506         } else if ( this.fieldLabel.length) {
13507 //                Roo.log(" label");
13508                  cfg.cn = [
13509                    indicator,
13510                     {
13511                         tag: 'label',
13512                         //cls : 'input-group-addon',
13513                         html : this.fieldLabel
13514                     },
13515                     combobox
13516                 ];
13517                 
13518                 if(this.indicatorpos == 'right'){
13519                     cfg.cn = [
13520                         {
13521                             tag: 'label',
13522                             //cls : 'input-group-addon',
13523                             html : this.fieldLabel
13524                         },
13525                         indicator,
13526                         combobox
13527                     ];
13528                     
13529                 }
13530
13531         } else {
13532             
13533 //                Roo.log(" no label && no align");
13534                 cfg = combobox
13535                      
13536                 
13537         }
13538          
13539         var settings=this;
13540         ['xs','sm','md','lg'].map(function(size){
13541             if (settings[size]) {
13542                 cfg.cls += ' col-' + size + '-' + settings[size];
13543             }
13544         });
13545         
13546         return cfg;
13547         
13548     },
13549     
13550     _initEventsCalled : false,
13551     
13552     // private
13553     initEvents: function()
13554     {   
13555         if (this._initEventsCalled) { // as we call render... prevent looping...
13556             return;
13557         }
13558         this._initEventsCalled = true;
13559         
13560         if (!this.store) {
13561             throw "can not find store for combo";
13562         }
13563         
13564         this.indicator = this.indicatorEl();
13565         
13566         this.store = Roo.factory(this.store, Roo.data);
13567         this.store.parent = this;
13568         
13569         // if we are building from html. then this element is so complex, that we can not really
13570         // use the rendered HTML.
13571         // so we have to trash and replace the previous code.
13572         if (Roo.XComponent.build_from_html) {
13573             // remove this element....
13574             var e = this.el.dom, k=0;
13575             while (e ) { e = e.previousSibling;  ++k;}
13576
13577             this.el.remove();
13578             
13579             this.el=false;
13580             this.rendered = false;
13581             
13582             this.render(this.parent().getChildContainer(true), k);
13583         }
13584         
13585         if(Roo.isIOS && this.useNativeIOS){
13586             this.initIOSView();
13587             return;
13588         }
13589         
13590         /*
13591          * Touch Devices
13592          */
13593         
13594         if(Roo.isTouch && this.mobileTouchView){
13595             this.initTouchView();
13596             return;
13597         }
13598         
13599         if(this.tickable){
13600             this.initTickableEvents();
13601             return;
13602         }
13603         
13604         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13605         
13606         if(this.hiddenName){
13607             
13608             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13609             
13610             this.hiddenField.dom.value =
13611                 this.hiddenValue !== undefined ? this.hiddenValue :
13612                 this.value !== undefined ? this.value : '';
13613
13614             // prevent input submission
13615             this.el.dom.removeAttribute('name');
13616             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13617              
13618              
13619         }
13620         //if(Roo.isGecko){
13621         //    this.el.dom.setAttribute('autocomplete', 'off');
13622         //}
13623         
13624         var cls = 'x-combo-list';
13625         
13626         //this.list = new Roo.Layer({
13627         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13628         //});
13629         
13630         var _this = this;
13631         
13632         (function(){
13633             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13634             _this.list.setWidth(lw);
13635         }).defer(100);
13636         
13637         this.list.on('mouseover', this.onViewOver, this);
13638         this.list.on('mousemove', this.onViewMove, this);
13639         this.list.on('scroll', this.onViewScroll, this);
13640         
13641         /*
13642         this.list.swallowEvent('mousewheel');
13643         this.assetHeight = 0;
13644
13645         if(this.title){
13646             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13647             this.assetHeight += this.header.getHeight();
13648         }
13649
13650         this.innerList = this.list.createChild({cls:cls+'-inner'});
13651         this.innerList.on('mouseover', this.onViewOver, this);
13652         this.innerList.on('mousemove', this.onViewMove, this);
13653         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13654         
13655         if(this.allowBlank && !this.pageSize && !this.disableClear){
13656             this.footer = this.list.createChild({cls:cls+'-ft'});
13657             this.pageTb = new Roo.Toolbar(this.footer);
13658            
13659         }
13660         if(this.pageSize){
13661             this.footer = this.list.createChild({cls:cls+'-ft'});
13662             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13663                     {pageSize: this.pageSize});
13664             
13665         }
13666         
13667         if (this.pageTb && this.allowBlank && !this.disableClear) {
13668             var _this = this;
13669             this.pageTb.add(new Roo.Toolbar.Fill(), {
13670                 cls: 'x-btn-icon x-btn-clear',
13671                 text: '&#160;',
13672                 handler: function()
13673                 {
13674                     _this.collapse();
13675                     _this.clearValue();
13676                     _this.onSelect(false, -1);
13677                 }
13678             });
13679         }
13680         if (this.footer) {
13681             this.assetHeight += this.footer.getHeight();
13682         }
13683         */
13684             
13685         if(!this.tpl){
13686             this.tpl = Roo.bootstrap.version == 4 ?
13687                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13688                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13689         }
13690
13691         this.view = new Roo.View(this.list, this.tpl, {
13692             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13693         });
13694         //this.view.wrapEl.setDisplayed(false);
13695         this.view.on('click', this.onViewClick, this);
13696         
13697         
13698         this.store.on('beforeload', this.onBeforeLoad, this);
13699         this.store.on('load', this.onLoad, this);
13700         this.store.on('loadexception', this.onLoadException, this);
13701         /*
13702         if(this.resizable){
13703             this.resizer = new Roo.Resizable(this.list,  {
13704                pinned:true, handles:'se'
13705             });
13706             this.resizer.on('resize', function(r, w, h){
13707                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13708                 this.listWidth = w;
13709                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13710                 this.restrictHeight();
13711             }, this);
13712             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13713         }
13714         */
13715         if(!this.editable){
13716             this.editable = true;
13717             this.setEditable(false);
13718         }
13719         
13720         /*
13721         
13722         if (typeof(this.events.add.listeners) != 'undefined') {
13723             
13724             this.addicon = this.wrap.createChild(
13725                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13726        
13727             this.addicon.on('click', function(e) {
13728                 this.fireEvent('add', this);
13729             }, this);
13730         }
13731         if (typeof(this.events.edit.listeners) != 'undefined') {
13732             
13733             this.editicon = this.wrap.createChild(
13734                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13735             if (this.addicon) {
13736                 this.editicon.setStyle('margin-left', '40px');
13737             }
13738             this.editicon.on('click', function(e) {
13739                 
13740                 // we fire even  if inothing is selected..
13741                 this.fireEvent('edit', this, this.lastData );
13742                 
13743             }, this);
13744         }
13745         */
13746         
13747         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13748             "up" : function(e){
13749                 this.inKeyMode = true;
13750                 this.selectPrev();
13751             },
13752
13753             "down" : function(e){
13754                 if(!this.isExpanded()){
13755                     this.onTriggerClick();
13756                 }else{
13757                     this.inKeyMode = true;
13758                     this.selectNext();
13759                 }
13760             },
13761
13762             "enter" : function(e){
13763 //                this.onViewClick();
13764                 //return true;
13765                 this.collapse();
13766                 
13767                 if(this.fireEvent("specialkey", this, e)){
13768                     this.onViewClick(false);
13769                 }
13770                 
13771                 return true;
13772             },
13773
13774             "esc" : function(e){
13775                 this.collapse();
13776             },
13777
13778             "tab" : function(e){
13779                 this.collapse();
13780                 
13781                 if(this.fireEvent("specialkey", this, e)){
13782                     this.onViewClick(false);
13783                 }
13784                 
13785                 return true;
13786             },
13787
13788             scope : this,
13789
13790             doRelay : function(foo, bar, hname){
13791                 if(hname == 'down' || this.scope.isExpanded()){
13792                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13793                 }
13794                 return true;
13795             },
13796
13797             forceKeyDown: true
13798         });
13799         
13800         
13801         this.queryDelay = Math.max(this.queryDelay || 10,
13802                 this.mode == 'local' ? 10 : 250);
13803         
13804         
13805         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13806         
13807         if(this.typeAhead){
13808             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13809         }
13810         if(this.editable !== false){
13811             this.inputEl().on("keyup", this.onKeyUp, this);
13812         }
13813         if(this.forceSelection){
13814             this.inputEl().on('blur', this.doForce, this);
13815         }
13816         
13817         if(this.multiple){
13818             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13819             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13820         }
13821     },
13822     
13823     initTickableEvents: function()
13824     {   
13825         this.createList();
13826         
13827         if(this.hiddenName){
13828             
13829             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13830             
13831             this.hiddenField.dom.value =
13832                 this.hiddenValue !== undefined ? this.hiddenValue :
13833                 this.value !== undefined ? this.value : '';
13834
13835             // prevent input submission
13836             this.el.dom.removeAttribute('name');
13837             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13838              
13839              
13840         }
13841         
13842 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13843         
13844         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13845         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13846         if(this.triggerList){
13847             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13848         }
13849          
13850         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13851         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13852         
13853         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13854         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13855         
13856         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13857         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13858         
13859         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13860         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13861         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13862         
13863         this.okBtn.hide();
13864         this.cancelBtn.hide();
13865         
13866         var _this = this;
13867         
13868         (function(){
13869             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13870             _this.list.setWidth(lw);
13871         }).defer(100);
13872         
13873         this.list.on('mouseover', this.onViewOver, this);
13874         this.list.on('mousemove', this.onViewMove, this);
13875         
13876         this.list.on('scroll', this.onViewScroll, this);
13877         
13878         if(!this.tpl){
13879             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13880                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13881         }
13882
13883         this.view = new Roo.View(this.list, this.tpl, {
13884             singleSelect:true,
13885             tickable:true,
13886             parent:this,
13887             store: this.store,
13888             selectedClass: this.selectedClass
13889         });
13890         
13891         //this.view.wrapEl.setDisplayed(false);
13892         this.view.on('click', this.onViewClick, this);
13893         
13894         
13895         
13896         this.store.on('beforeload', this.onBeforeLoad, this);
13897         this.store.on('load', this.onLoad, this);
13898         this.store.on('loadexception', this.onLoadException, this);
13899         
13900         if(this.editable){
13901             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13902                 "up" : function(e){
13903                     this.inKeyMode = true;
13904                     this.selectPrev();
13905                 },
13906
13907                 "down" : function(e){
13908                     this.inKeyMode = true;
13909                     this.selectNext();
13910                 },
13911
13912                 "enter" : function(e){
13913                     if(this.fireEvent("specialkey", this, e)){
13914                         this.onViewClick(false);
13915                     }
13916                     
13917                     return true;
13918                 },
13919
13920                 "esc" : function(e){
13921                     this.onTickableFooterButtonClick(e, false, false);
13922                 },
13923
13924                 "tab" : function(e){
13925                     this.fireEvent("specialkey", this, e);
13926                     
13927                     this.onTickableFooterButtonClick(e, false, false);
13928                     
13929                     return true;
13930                 },
13931
13932                 scope : this,
13933
13934                 doRelay : function(e, fn, key){
13935                     if(this.scope.isExpanded()){
13936                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13937                     }
13938                     return true;
13939                 },
13940
13941                 forceKeyDown: true
13942             });
13943         }
13944         
13945         this.queryDelay = Math.max(this.queryDelay || 10,
13946                 this.mode == 'local' ? 10 : 250);
13947         
13948         
13949         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13950         
13951         if(this.typeAhead){
13952             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13953         }
13954         
13955         if(this.editable !== false){
13956             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13957         }
13958         
13959         this.indicator = this.indicatorEl();
13960         
13961         if(this.indicator){
13962             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13963             this.indicator.hide();
13964         }
13965         
13966     },
13967
13968     onDestroy : function(){
13969         if(this.view){
13970             this.view.setStore(null);
13971             this.view.el.removeAllListeners();
13972             this.view.el.remove();
13973             this.view.purgeListeners();
13974         }
13975         if(this.list){
13976             this.list.dom.innerHTML  = '';
13977         }
13978         
13979         if(this.store){
13980             this.store.un('beforeload', this.onBeforeLoad, this);
13981             this.store.un('load', this.onLoad, this);
13982             this.store.un('loadexception', this.onLoadException, this);
13983         }
13984         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13985     },
13986
13987     // private
13988     fireKey : function(e){
13989         if(e.isNavKeyPress() && !this.list.isVisible()){
13990             this.fireEvent("specialkey", this, e);
13991         }
13992     },
13993
13994     // private
13995     onResize: function(w, h){
13996 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13997 //        
13998 //        if(typeof w != 'number'){
13999 //            // we do not handle it!?!?
14000 //            return;
14001 //        }
14002 //        var tw = this.trigger.getWidth();
14003 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14004 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14005 //        var x = w - tw;
14006 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14007 //            
14008 //        //this.trigger.setStyle('left', x+'px');
14009 //        
14010 //        if(this.list && this.listWidth === undefined){
14011 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14012 //            this.list.setWidth(lw);
14013 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14014 //        }
14015         
14016     
14017         
14018     },
14019
14020     /**
14021      * Allow or prevent the user from directly editing the field text.  If false is passed,
14022      * the user will only be able to select from the items defined in the dropdown list.  This method
14023      * is the runtime equivalent of setting the 'editable' config option at config time.
14024      * @param {Boolean} value True to allow the user to directly edit the field text
14025      */
14026     setEditable : function(value){
14027         if(value == this.editable){
14028             return;
14029         }
14030         this.editable = value;
14031         if(!value){
14032             this.inputEl().dom.setAttribute('readOnly', true);
14033             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14034             this.inputEl().addClass('x-combo-noedit');
14035         }else{
14036             this.inputEl().dom.setAttribute('readOnly', false);
14037             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14038             this.inputEl().removeClass('x-combo-noedit');
14039         }
14040     },
14041
14042     // private
14043     
14044     onBeforeLoad : function(combo,opts){
14045         if(!this.hasFocus){
14046             return;
14047         }
14048          if (!opts.add) {
14049             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14050          }
14051         this.restrictHeight();
14052         this.selectedIndex = -1;
14053     },
14054
14055     // private
14056     onLoad : function(){
14057         
14058         this.hasQuery = false;
14059         
14060         if(!this.hasFocus){
14061             return;
14062         }
14063         
14064         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14065             this.loading.hide();
14066         }
14067         
14068         if(this.store.getCount() > 0){
14069             
14070             this.expand();
14071             this.restrictHeight();
14072             if(this.lastQuery == this.allQuery){
14073                 if(this.editable && !this.tickable){
14074                     this.inputEl().dom.select();
14075                 }
14076                 
14077                 if(
14078                     !this.selectByValue(this.value, true) &&
14079                     this.autoFocus && 
14080                     (
14081                         !this.store.lastOptions ||
14082                         typeof(this.store.lastOptions.add) == 'undefined' || 
14083                         this.store.lastOptions.add != true
14084                     )
14085                 ){
14086                     this.select(0, true);
14087                 }
14088             }else{
14089                 if(this.autoFocus){
14090                     this.selectNext();
14091                 }
14092                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14093                     this.taTask.delay(this.typeAheadDelay);
14094                 }
14095             }
14096         }else{
14097             this.onEmptyResults();
14098         }
14099         
14100         //this.el.focus();
14101     },
14102     // private
14103     onLoadException : function()
14104     {
14105         this.hasQuery = false;
14106         
14107         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14108             this.loading.hide();
14109         }
14110         
14111         if(this.tickable && this.editable){
14112             return;
14113         }
14114         
14115         this.collapse();
14116         // only causes errors at present
14117         //Roo.log(this.store.reader.jsonData);
14118         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14119             // fixme
14120             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14121         //}
14122         
14123         
14124     },
14125     // private
14126     onTypeAhead : function(){
14127         if(this.store.getCount() > 0){
14128             var r = this.store.getAt(0);
14129             var newValue = r.data[this.displayField];
14130             var len = newValue.length;
14131             var selStart = this.getRawValue().length;
14132             
14133             if(selStart != len){
14134                 this.setRawValue(newValue);
14135                 this.selectText(selStart, newValue.length);
14136             }
14137         }
14138     },
14139
14140     // private
14141     onSelect : function(record, index){
14142         
14143         if(this.fireEvent('beforeselect', this, record, index) !== false){
14144         
14145             this.setFromData(index > -1 ? record.data : false);
14146             
14147             this.collapse();
14148             this.fireEvent('select', this, record, index);
14149         }
14150     },
14151
14152     /**
14153      * Returns the currently selected field value or empty string if no value is set.
14154      * @return {String} value The selected value
14155      */
14156     getValue : function()
14157     {
14158         if(Roo.isIOS && this.useNativeIOS){
14159             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14160         }
14161         
14162         if(this.multiple){
14163             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14164         }
14165         
14166         if(this.valueField){
14167             return typeof this.value != 'undefined' ? this.value : '';
14168         }else{
14169             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14170         }
14171     },
14172     
14173     getRawValue : function()
14174     {
14175         if(Roo.isIOS && this.useNativeIOS){
14176             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14177         }
14178         
14179         var v = this.inputEl().getValue();
14180         
14181         return v;
14182     },
14183
14184     /**
14185      * Clears any text/value currently set in the field
14186      */
14187     clearValue : function(){
14188         
14189         if(this.hiddenField){
14190             this.hiddenField.dom.value = '';
14191         }
14192         this.value = '';
14193         this.setRawValue('');
14194         this.lastSelectionText = '';
14195         this.lastData = false;
14196         
14197         var close = this.closeTriggerEl();
14198         
14199         if(close){
14200             close.hide();
14201         }
14202         
14203         this.validate();
14204         
14205     },
14206
14207     /**
14208      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14209      * will be displayed in the field.  If the value does not match the data value of an existing item,
14210      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14211      * Otherwise the field will be blank (although the value will still be set).
14212      * @param {String} value The value to match
14213      */
14214     setValue : function(v)
14215     {
14216         if(Roo.isIOS && this.useNativeIOS){
14217             this.setIOSValue(v);
14218             return;
14219         }
14220         
14221         if(this.multiple){
14222             this.syncValue();
14223             return;
14224         }
14225         
14226         var text = v;
14227         if(this.valueField){
14228             var r = this.findRecord(this.valueField, v);
14229             if(r){
14230                 text = r.data[this.displayField];
14231             }else if(this.valueNotFoundText !== undefined){
14232                 text = this.valueNotFoundText;
14233             }
14234         }
14235         this.lastSelectionText = text;
14236         if(this.hiddenField){
14237             this.hiddenField.dom.value = v;
14238         }
14239         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14240         this.value = v;
14241         
14242         var close = this.closeTriggerEl();
14243         
14244         if(close){
14245             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14246         }
14247         
14248         this.validate();
14249     },
14250     /**
14251      * @property {Object} the last set data for the element
14252      */
14253     
14254     lastData : false,
14255     /**
14256      * Sets the value of the field based on a object which is related to the record format for the store.
14257      * @param {Object} value the value to set as. or false on reset?
14258      */
14259     setFromData : function(o){
14260         
14261         if(this.multiple){
14262             this.addItem(o);
14263             return;
14264         }
14265             
14266         var dv = ''; // display value
14267         var vv = ''; // value value..
14268         this.lastData = o;
14269         if (this.displayField) {
14270             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14271         } else {
14272             // this is an error condition!!!
14273             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14274         }
14275         
14276         if(this.valueField){
14277             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14278         }
14279         
14280         var close = this.closeTriggerEl();
14281         
14282         if(close){
14283             if(dv.length || vv * 1 > 0){
14284                 close.show() ;
14285                 this.blockFocus=true;
14286             } else {
14287                 close.hide();
14288             }             
14289         }
14290         
14291         if(this.hiddenField){
14292             this.hiddenField.dom.value = vv;
14293             
14294             this.lastSelectionText = dv;
14295             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14296             this.value = vv;
14297             return;
14298         }
14299         // no hidden field.. - we store the value in 'value', but still display
14300         // display field!!!!
14301         this.lastSelectionText = dv;
14302         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14303         this.value = vv;
14304         
14305         
14306         
14307     },
14308     // private
14309     reset : function(){
14310         // overridden so that last data is reset..
14311         
14312         if(this.multiple){
14313             this.clearItem();
14314             return;
14315         }
14316         
14317         this.setValue(this.originalValue);
14318         //this.clearInvalid();
14319         this.lastData = false;
14320         if (this.view) {
14321             this.view.clearSelections();
14322         }
14323         
14324         this.validate();
14325     },
14326     // private
14327     findRecord : function(prop, value){
14328         var record;
14329         if(this.store.getCount() > 0){
14330             this.store.each(function(r){
14331                 if(r.data[prop] == value){
14332                     record = r;
14333                     return false;
14334                 }
14335                 return true;
14336             });
14337         }
14338         return record;
14339     },
14340     
14341     getName: function()
14342     {
14343         // returns hidden if it's set..
14344         if (!this.rendered) {return ''};
14345         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14346         
14347     },
14348     // private
14349     onViewMove : function(e, t){
14350         this.inKeyMode = false;
14351     },
14352
14353     // private
14354     onViewOver : function(e, t){
14355         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14356             return;
14357         }
14358         var item = this.view.findItemFromChild(t);
14359         
14360         if(item){
14361             var index = this.view.indexOf(item);
14362             this.select(index, false);
14363         }
14364     },
14365
14366     // private
14367     onViewClick : function(view, doFocus, el, e)
14368     {
14369         var index = this.view.getSelectedIndexes()[0];
14370         
14371         var r = this.store.getAt(index);
14372         
14373         if(this.tickable){
14374             
14375             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14376                 return;
14377             }
14378             
14379             var rm = false;
14380             var _this = this;
14381             
14382             Roo.each(this.tickItems, function(v,k){
14383                 
14384                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14385                     Roo.log(v);
14386                     _this.tickItems.splice(k, 1);
14387                     
14388                     if(typeof(e) == 'undefined' && view == false){
14389                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14390                     }
14391                     
14392                     rm = true;
14393                     return;
14394                 }
14395             });
14396             
14397             if(rm){
14398                 return;
14399             }
14400             
14401             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14402                 this.tickItems.push(r.data);
14403             }
14404             
14405             if(typeof(e) == 'undefined' && view == false){
14406                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14407             }
14408                     
14409             return;
14410         }
14411         
14412         if(r){
14413             this.onSelect(r, index);
14414         }
14415         if(doFocus !== false && !this.blockFocus){
14416             this.inputEl().focus();
14417         }
14418     },
14419
14420     // private
14421     restrictHeight : function(){
14422         //this.innerList.dom.style.height = '';
14423         //var inner = this.innerList.dom;
14424         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14425         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14426         //this.list.beginUpdate();
14427         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14428         this.list.alignTo(this.inputEl(), this.listAlign);
14429         this.list.alignTo(this.inputEl(), this.listAlign);
14430         //this.list.endUpdate();
14431     },
14432
14433     // private
14434     onEmptyResults : function(){
14435         
14436         if(this.tickable && this.editable){
14437             this.hasFocus = false;
14438             this.restrictHeight();
14439             return;
14440         }
14441         
14442         this.collapse();
14443     },
14444
14445     /**
14446      * Returns true if the dropdown list is expanded, else false.
14447      */
14448     isExpanded : function(){
14449         return this.list.isVisible();
14450     },
14451
14452     /**
14453      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14454      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14455      * @param {String} value The data value of the item to select
14456      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14457      * selected item if it is not currently in view (defaults to true)
14458      * @return {Boolean} True if the value matched an item in the list, else false
14459      */
14460     selectByValue : function(v, scrollIntoView){
14461         if(v !== undefined && v !== null){
14462             var r = this.findRecord(this.valueField || this.displayField, v);
14463             if(r){
14464                 this.select(this.store.indexOf(r), scrollIntoView);
14465                 return true;
14466             }
14467         }
14468         return false;
14469     },
14470
14471     /**
14472      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14473      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14474      * @param {Number} index The zero-based index of the list item to select
14475      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14476      * selected item if it is not currently in view (defaults to true)
14477      */
14478     select : function(index, scrollIntoView){
14479         this.selectedIndex = index;
14480         this.view.select(index);
14481         if(scrollIntoView !== false){
14482             var el = this.view.getNode(index);
14483             /*
14484              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14485              */
14486             if(el){
14487                 this.list.scrollChildIntoView(el, false);
14488             }
14489         }
14490     },
14491
14492     // private
14493     selectNext : function(){
14494         var ct = this.store.getCount();
14495         if(ct > 0){
14496             if(this.selectedIndex == -1){
14497                 this.select(0);
14498             }else if(this.selectedIndex < ct-1){
14499                 this.select(this.selectedIndex+1);
14500             }
14501         }
14502     },
14503
14504     // private
14505     selectPrev : function(){
14506         var ct = this.store.getCount();
14507         if(ct > 0){
14508             if(this.selectedIndex == -1){
14509                 this.select(0);
14510             }else if(this.selectedIndex != 0){
14511                 this.select(this.selectedIndex-1);
14512             }
14513         }
14514     },
14515
14516     // private
14517     onKeyUp : function(e){
14518         if(this.editable !== false && !e.isSpecialKey()){
14519             this.lastKey = e.getKey();
14520             this.dqTask.delay(this.queryDelay);
14521         }
14522     },
14523
14524     // private
14525     validateBlur : function(){
14526         return !this.list || !this.list.isVisible();   
14527     },
14528
14529     // private
14530     initQuery : function(){
14531         
14532         var v = this.getRawValue();
14533         
14534         if(this.tickable && this.editable){
14535             v = this.tickableInputEl().getValue();
14536         }
14537         
14538         this.doQuery(v);
14539     },
14540
14541     // private
14542     doForce : function(){
14543         if(this.inputEl().dom.value.length > 0){
14544             this.inputEl().dom.value =
14545                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14546              
14547         }
14548     },
14549
14550     /**
14551      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14552      * query allowing the query action to be canceled if needed.
14553      * @param {String} query The SQL query to execute
14554      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14555      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14556      * saved in the current store (defaults to false)
14557      */
14558     doQuery : function(q, forceAll){
14559         
14560         if(q === undefined || q === null){
14561             q = '';
14562         }
14563         var qe = {
14564             query: q,
14565             forceAll: forceAll,
14566             combo: this,
14567             cancel:false
14568         };
14569         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14570             return false;
14571         }
14572         q = qe.query;
14573         
14574         forceAll = qe.forceAll;
14575         if(forceAll === true || (q.length >= this.minChars)){
14576             
14577             this.hasQuery = true;
14578             
14579             if(this.lastQuery != q || this.alwaysQuery){
14580                 this.lastQuery = q;
14581                 if(this.mode == 'local'){
14582                     this.selectedIndex = -1;
14583                     if(forceAll){
14584                         this.store.clearFilter();
14585                     }else{
14586                         
14587                         if(this.specialFilter){
14588                             this.fireEvent('specialfilter', this);
14589                             this.onLoad();
14590                             return;
14591                         }
14592                         
14593                         this.store.filter(this.displayField, q);
14594                     }
14595                     
14596                     this.store.fireEvent("datachanged", this.store);
14597                     
14598                     this.onLoad();
14599                     
14600                     
14601                 }else{
14602                     
14603                     this.store.baseParams[this.queryParam] = q;
14604                     
14605                     var options = {params : this.getParams(q)};
14606                     
14607                     if(this.loadNext){
14608                         options.add = true;
14609                         options.params.start = this.page * this.pageSize;
14610                     }
14611                     
14612                     this.store.load(options);
14613                     
14614                     /*
14615                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14616                      *  we should expand the list on onLoad
14617                      *  so command out it
14618                      */
14619 //                    this.expand();
14620                 }
14621             }else{
14622                 this.selectedIndex = -1;
14623                 this.onLoad();   
14624             }
14625         }
14626         
14627         this.loadNext = false;
14628     },
14629     
14630     // private
14631     getParams : function(q){
14632         var p = {};
14633         //p[this.queryParam] = q;
14634         
14635         if(this.pageSize){
14636             p.start = 0;
14637             p.limit = this.pageSize;
14638         }
14639         return p;
14640     },
14641
14642     /**
14643      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14644      */
14645     collapse : function(){
14646         if(!this.isExpanded()){
14647             return;
14648         }
14649         
14650         this.list.hide();
14651         
14652         this.hasFocus = false;
14653         
14654         if(this.tickable){
14655             this.okBtn.hide();
14656             this.cancelBtn.hide();
14657             this.trigger.show();
14658             
14659             if(this.editable){
14660                 this.tickableInputEl().dom.value = '';
14661                 this.tickableInputEl().blur();
14662             }
14663             
14664         }
14665         
14666         Roo.get(document).un('mousedown', this.collapseIf, this);
14667         Roo.get(document).un('mousewheel', this.collapseIf, this);
14668         if (!this.editable) {
14669             Roo.get(document).un('keydown', this.listKeyPress, this);
14670         }
14671         this.fireEvent('collapse', this);
14672         
14673         this.validate();
14674     },
14675
14676     // private
14677     collapseIf : function(e){
14678         var in_combo  = e.within(this.el);
14679         var in_list =  e.within(this.list);
14680         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14681         
14682         if (in_combo || in_list || is_list) {
14683             //e.stopPropagation();
14684             return;
14685         }
14686         
14687         if(this.tickable){
14688             this.onTickableFooterButtonClick(e, false, false);
14689         }
14690
14691         this.collapse();
14692         
14693     },
14694
14695     /**
14696      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14697      */
14698     expand : function(){
14699        
14700         if(this.isExpanded() || !this.hasFocus){
14701             return;
14702         }
14703         
14704         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14705         this.list.setWidth(lw);
14706         
14707         Roo.log('expand');
14708         
14709         this.list.show();
14710         
14711         this.restrictHeight();
14712         
14713         if(this.tickable){
14714             
14715             this.tickItems = Roo.apply([], this.item);
14716             
14717             this.okBtn.show();
14718             this.cancelBtn.show();
14719             this.trigger.hide();
14720             
14721             if(this.editable){
14722                 this.tickableInputEl().focus();
14723             }
14724             
14725         }
14726         
14727         Roo.get(document).on('mousedown', this.collapseIf, this);
14728         Roo.get(document).on('mousewheel', this.collapseIf, this);
14729         if (!this.editable) {
14730             Roo.get(document).on('keydown', this.listKeyPress, this);
14731         }
14732         
14733         this.fireEvent('expand', this);
14734     },
14735
14736     // private
14737     // Implements the default empty TriggerField.onTriggerClick function
14738     onTriggerClick : function(e)
14739     {
14740         Roo.log('trigger click');
14741         
14742         if(this.disabled || !this.triggerList){
14743             return;
14744         }
14745         
14746         this.page = 0;
14747         this.loadNext = false;
14748         
14749         if(this.isExpanded()){
14750             this.collapse();
14751             if (!this.blockFocus) {
14752                 this.inputEl().focus();
14753             }
14754             
14755         }else {
14756             this.hasFocus = true;
14757             if(this.triggerAction == 'all') {
14758                 this.doQuery(this.allQuery, true);
14759             } else {
14760                 this.doQuery(this.getRawValue());
14761             }
14762             if (!this.blockFocus) {
14763                 this.inputEl().focus();
14764             }
14765         }
14766     },
14767     
14768     onTickableTriggerClick : function(e)
14769     {
14770         if(this.disabled){
14771             return;
14772         }
14773         
14774         this.page = 0;
14775         this.loadNext = false;
14776         this.hasFocus = true;
14777         
14778         if(this.triggerAction == 'all') {
14779             this.doQuery(this.allQuery, true);
14780         } else {
14781             this.doQuery(this.getRawValue());
14782         }
14783     },
14784     
14785     onSearchFieldClick : function(e)
14786     {
14787         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14788             this.onTickableFooterButtonClick(e, false, false);
14789             return;
14790         }
14791         
14792         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14793             return;
14794         }
14795         
14796         this.page = 0;
14797         this.loadNext = false;
14798         this.hasFocus = true;
14799         
14800         if(this.triggerAction == 'all') {
14801             this.doQuery(this.allQuery, true);
14802         } else {
14803             this.doQuery(this.getRawValue());
14804         }
14805     },
14806     
14807     listKeyPress : function(e)
14808     {
14809         //Roo.log('listkeypress');
14810         // scroll to first matching element based on key pres..
14811         if (e.isSpecialKey()) {
14812             return false;
14813         }
14814         var k = String.fromCharCode(e.getKey()).toUpperCase();
14815         //Roo.log(k);
14816         var match  = false;
14817         var csel = this.view.getSelectedNodes();
14818         var cselitem = false;
14819         if (csel.length) {
14820             var ix = this.view.indexOf(csel[0]);
14821             cselitem  = this.store.getAt(ix);
14822             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14823                 cselitem = false;
14824             }
14825             
14826         }
14827         
14828         this.store.each(function(v) { 
14829             if (cselitem) {
14830                 // start at existing selection.
14831                 if (cselitem.id == v.id) {
14832                     cselitem = false;
14833                 }
14834                 return true;
14835             }
14836                 
14837             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14838                 match = this.store.indexOf(v);
14839                 return false;
14840             }
14841             return true;
14842         }, this);
14843         
14844         if (match === false) {
14845             return true; // no more action?
14846         }
14847         // scroll to?
14848         this.view.select(match);
14849         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14850         sn.scrollIntoView(sn.dom.parentNode, false);
14851     },
14852     
14853     onViewScroll : function(e, t){
14854         
14855         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){
14856             return;
14857         }
14858         
14859         this.hasQuery = true;
14860         
14861         this.loading = this.list.select('.loading', true).first();
14862         
14863         if(this.loading === null){
14864             this.list.createChild({
14865                 tag: 'div',
14866                 cls: 'loading roo-select2-more-results roo-select2-active',
14867                 html: 'Loading more results...'
14868             });
14869             
14870             this.loading = this.list.select('.loading', true).first();
14871             
14872             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14873             
14874             this.loading.hide();
14875         }
14876         
14877         this.loading.show();
14878         
14879         var _combo = this;
14880         
14881         this.page++;
14882         this.loadNext = true;
14883         
14884         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14885         
14886         return;
14887     },
14888     
14889     addItem : function(o)
14890     {   
14891         var dv = ''; // display value
14892         
14893         if (this.displayField) {
14894             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14895         } else {
14896             // this is an error condition!!!
14897             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14898         }
14899         
14900         if(!dv.length){
14901             return;
14902         }
14903         
14904         var choice = this.choices.createChild({
14905             tag: 'li',
14906             cls: 'roo-select2-search-choice',
14907             cn: [
14908                 {
14909                     tag: 'div',
14910                     html: dv
14911                 },
14912                 {
14913                     tag: 'a',
14914                     href: '#',
14915                     cls: 'roo-select2-search-choice-close fa fa-times',
14916                     tabindex: '-1'
14917                 }
14918             ]
14919             
14920         }, this.searchField);
14921         
14922         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14923         
14924         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14925         
14926         this.item.push(o);
14927         
14928         this.lastData = o;
14929         
14930         this.syncValue();
14931         
14932         this.inputEl().dom.value = '';
14933         
14934         this.validate();
14935     },
14936     
14937     onRemoveItem : function(e, _self, o)
14938     {
14939         e.preventDefault();
14940         
14941         this.lastItem = Roo.apply([], this.item);
14942         
14943         var index = this.item.indexOf(o.data) * 1;
14944         
14945         if( index < 0){
14946             Roo.log('not this item?!');
14947             return;
14948         }
14949         
14950         this.item.splice(index, 1);
14951         o.item.remove();
14952         
14953         this.syncValue();
14954         
14955         this.fireEvent('remove', this, e);
14956         
14957         this.validate();
14958         
14959     },
14960     
14961     syncValue : function()
14962     {
14963         if(!this.item.length){
14964             this.clearValue();
14965             return;
14966         }
14967             
14968         var value = [];
14969         var _this = this;
14970         Roo.each(this.item, function(i){
14971             if(_this.valueField){
14972                 value.push(i[_this.valueField]);
14973                 return;
14974             }
14975
14976             value.push(i);
14977         });
14978
14979         this.value = value.join(',');
14980
14981         if(this.hiddenField){
14982             this.hiddenField.dom.value = this.value;
14983         }
14984         
14985         this.store.fireEvent("datachanged", this.store);
14986         
14987         this.validate();
14988     },
14989     
14990     clearItem : function()
14991     {
14992         if(!this.multiple){
14993             return;
14994         }
14995         
14996         this.item = [];
14997         
14998         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14999            c.remove();
15000         });
15001         
15002         this.syncValue();
15003         
15004         this.validate();
15005         
15006         if(this.tickable && !Roo.isTouch){
15007             this.view.refresh();
15008         }
15009     },
15010     
15011     inputEl: function ()
15012     {
15013         if(Roo.isIOS && this.useNativeIOS){
15014             return this.el.select('select.roo-ios-select', true).first();
15015         }
15016         
15017         if(Roo.isTouch && this.mobileTouchView){
15018             return this.el.select('input.form-control',true).first();
15019         }
15020         
15021         if(this.tickable){
15022             return this.searchField;
15023         }
15024         
15025         return this.el.select('input.form-control',true).first();
15026     },
15027     
15028     onTickableFooterButtonClick : function(e, btn, el)
15029     {
15030         e.preventDefault();
15031         
15032         this.lastItem = Roo.apply([], this.item);
15033         
15034         if(btn && btn.name == 'cancel'){
15035             this.tickItems = Roo.apply([], this.item);
15036             this.collapse();
15037             return;
15038         }
15039         
15040         this.clearItem();
15041         
15042         var _this = this;
15043         
15044         Roo.each(this.tickItems, function(o){
15045             _this.addItem(o);
15046         });
15047         
15048         this.collapse();
15049         
15050     },
15051     
15052     validate : function()
15053     {
15054         if(this.getVisibilityEl().hasClass('hidden')){
15055             return true;
15056         }
15057         
15058         var v = this.getRawValue();
15059         
15060         if(this.multiple){
15061             v = this.getValue();
15062         }
15063         
15064         if(this.disabled || this.allowBlank || v.length){
15065             this.markValid();
15066             return true;
15067         }
15068         
15069         this.markInvalid();
15070         return false;
15071     },
15072     
15073     tickableInputEl : function()
15074     {
15075         if(!this.tickable || !this.editable){
15076             return this.inputEl();
15077         }
15078         
15079         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15080     },
15081     
15082     
15083     getAutoCreateTouchView : function()
15084     {
15085         var id = Roo.id();
15086         
15087         var cfg = {
15088             cls: 'form-group' //input-group
15089         };
15090         
15091         var input =  {
15092             tag: 'input',
15093             id : id,
15094             type : this.inputType,
15095             cls : 'form-control x-combo-noedit',
15096             autocomplete: 'new-password',
15097             placeholder : this.placeholder || '',
15098             readonly : true
15099         };
15100         
15101         if (this.name) {
15102             input.name = this.name;
15103         }
15104         
15105         if (this.size) {
15106             input.cls += ' input-' + this.size;
15107         }
15108         
15109         if (this.disabled) {
15110             input.disabled = true;
15111         }
15112         
15113         var inputblock = {
15114             cls : '',
15115             cn : [
15116                 input
15117             ]
15118         };
15119         
15120         if(this.before){
15121             inputblock.cls += ' input-group';
15122             
15123             inputblock.cn.unshift({
15124                 tag :'span',
15125                 cls : 'input-group-addon input-group-prepend input-group-text',
15126                 html : this.before
15127             });
15128         }
15129         
15130         if(this.removable && !this.multiple){
15131             inputblock.cls += ' roo-removable';
15132             
15133             inputblock.cn.push({
15134                 tag: 'button',
15135                 html : 'x',
15136                 cls : 'roo-combo-removable-btn close'
15137             });
15138         }
15139
15140         if(this.hasFeedback && !this.allowBlank){
15141             
15142             inputblock.cls += ' has-feedback';
15143             
15144             inputblock.cn.push({
15145                 tag: 'span',
15146                 cls: 'glyphicon form-control-feedback'
15147             });
15148             
15149         }
15150         
15151         if (this.after) {
15152             
15153             inputblock.cls += (this.before) ? '' : ' input-group';
15154             
15155             inputblock.cn.push({
15156                 tag :'span',
15157                 cls : 'input-group-addon input-group-append input-group-text',
15158                 html : this.after
15159             });
15160         }
15161
15162         
15163         var ibwrap = inputblock;
15164         
15165         if(this.multiple){
15166             ibwrap = {
15167                 tag: 'ul',
15168                 cls: 'roo-select2-choices',
15169                 cn:[
15170                     {
15171                         tag: 'li',
15172                         cls: 'roo-select2-search-field',
15173                         cn: [
15174
15175                             inputblock
15176                         ]
15177                     }
15178                 ]
15179             };
15180         
15181             
15182         }
15183         
15184         var combobox = {
15185             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15186             cn: [
15187                 {
15188                     tag: 'input',
15189                     type : 'hidden',
15190                     cls: 'form-hidden-field'
15191                 },
15192                 ibwrap
15193             ]
15194         };
15195         
15196         if(!this.multiple && this.showToggleBtn){
15197             
15198             var caret = {
15199                         tag: 'span',
15200                         cls: 'caret'
15201             };
15202             
15203             if (this.caret != false) {
15204                 caret = {
15205                      tag: 'i',
15206                      cls: 'fa fa-' + this.caret
15207                 };
15208                 
15209             }
15210             
15211             combobox.cn.push({
15212                 tag :'span',
15213                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15214                 cn : [
15215                     caret,
15216                     {
15217                         tag: 'span',
15218                         cls: 'combobox-clear',
15219                         cn  : [
15220                             {
15221                                 tag : 'i',
15222                                 cls: 'icon-remove'
15223                             }
15224                         ]
15225                     }
15226                 ]
15227
15228             })
15229         }
15230         
15231         if(this.multiple){
15232             combobox.cls += ' roo-select2-container-multi';
15233         }
15234         
15235         var align = this.labelAlign || this.parentLabelAlign();
15236         
15237         if (align ==='left' && this.fieldLabel.length) {
15238
15239             cfg.cn = [
15240                 {
15241                    tag : 'i',
15242                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15243                    tooltip : 'This field is required'
15244                 },
15245                 {
15246                     tag: 'label',
15247                     cls : 'control-label col-form-label',
15248                     html : this.fieldLabel
15249
15250                 },
15251                 {
15252                     cls : '', 
15253                     cn: [
15254                         combobox
15255                     ]
15256                 }
15257             ];
15258             
15259             var labelCfg = cfg.cn[1];
15260             var contentCfg = cfg.cn[2];
15261             
15262
15263             if(this.indicatorpos == 'right'){
15264                 cfg.cn = [
15265                     {
15266                         tag: 'label',
15267                         'for' :  id,
15268                         cls : 'control-label col-form-label',
15269                         cn : [
15270                             {
15271                                 tag : 'span',
15272                                 html : this.fieldLabel
15273                             },
15274                             {
15275                                 tag : 'i',
15276                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15277                                 tooltip : 'This field is required'
15278                             }
15279                         ]
15280                     },
15281                     {
15282                         cls : "",
15283                         cn: [
15284                             combobox
15285                         ]
15286                     }
15287
15288                 ];
15289                 
15290                 labelCfg = cfg.cn[0];
15291                 contentCfg = cfg.cn[1];
15292             }
15293             
15294            
15295             
15296             if(this.labelWidth > 12){
15297                 labelCfg.style = "width: " + this.labelWidth + 'px';
15298             }
15299             
15300             if(this.labelWidth < 13 && this.labelmd == 0){
15301                 this.labelmd = this.labelWidth;
15302             }
15303             
15304             if(this.labellg > 0){
15305                 labelCfg.cls += ' col-lg-' + this.labellg;
15306                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15307             }
15308             
15309             if(this.labelmd > 0){
15310                 labelCfg.cls += ' col-md-' + this.labelmd;
15311                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15312             }
15313             
15314             if(this.labelsm > 0){
15315                 labelCfg.cls += ' col-sm-' + this.labelsm;
15316                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15317             }
15318             
15319             if(this.labelxs > 0){
15320                 labelCfg.cls += ' col-xs-' + this.labelxs;
15321                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15322             }
15323                 
15324                 
15325         } else if ( this.fieldLabel.length) {
15326             cfg.cn = [
15327                 {
15328                    tag : 'i',
15329                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15330                    tooltip : 'This field is required'
15331                 },
15332                 {
15333                     tag: 'label',
15334                     cls : 'control-label',
15335                     html : this.fieldLabel
15336
15337                 },
15338                 {
15339                     cls : '', 
15340                     cn: [
15341                         combobox
15342                     ]
15343                 }
15344             ];
15345             
15346             if(this.indicatorpos == 'right'){
15347                 cfg.cn = [
15348                     {
15349                         tag: 'label',
15350                         cls : 'control-label',
15351                         html : this.fieldLabel,
15352                         cn : [
15353                             {
15354                                tag : 'i',
15355                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15356                                tooltip : 'This field is required'
15357                             }
15358                         ]
15359                     },
15360                     {
15361                         cls : '', 
15362                         cn: [
15363                             combobox
15364                         ]
15365                     }
15366                 ];
15367             }
15368         } else {
15369             cfg.cn = combobox;    
15370         }
15371         
15372         
15373         var settings = this;
15374         
15375         ['xs','sm','md','lg'].map(function(size){
15376             if (settings[size]) {
15377                 cfg.cls += ' col-' + size + '-' + settings[size];
15378             }
15379         });
15380         
15381         return cfg;
15382     },
15383     
15384     initTouchView : function()
15385     {
15386         this.renderTouchView();
15387         
15388         this.touchViewEl.on('scroll', function(){
15389             this.el.dom.scrollTop = 0;
15390         }, this);
15391         
15392         this.originalValue = this.getValue();
15393         
15394         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15395         
15396         this.inputEl().on("click", this.showTouchView, this);
15397         if (this.triggerEl) {
15398             this.triggerEl.on("click", this.showTouchView, this);
15399         }
15400         
15401         
15402         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15403         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15404         
15405         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15406         
15407         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15408         this.store.on('load', this.onTouchViewLoad, this);
15409         this.store.on('loadexception', this.onTouchViewLoadException, this);
15410         
15411         if(this.hiddenName){
15412             
15413             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15414             
15415             this.hiddenField.dom.value =
15416                 this.hiddenValue !== undefined ? this.hiddenValue :
15417                 this.value !== undefined ? this.value : '';
15418         
15419             this.el.dom.removeAttribute('name');
15420             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15421         }
15422         
15423         if(this.multiple){
15424             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15425             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15426         }
15427         
15428         if(this.removable && !this.multiple){
15429             var close = this.closeTriggerEl();
15430             if(close){
15431                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15432                 close.on('click', this.removeBtnClick, this, close);
15433             }
15434         }
15435         /*
15436          * fix the bug in Safari iOS8
15437          */
15438         this.inputEl().on("focus", function(e){
15439             document.activeElement.blur();
15440         }, this);
15441         
15442         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15443         
15444         return;
15445         
15446         
15447     },
15448     
15449     renderTouchView : function()
15450     {
15451         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15452         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15453         
15454         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15455         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15456         
15457         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15458         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15459         this.touchViewBodyEl.setStyle('overflow', 'auto');
15460         
15461         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15462         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15463         
15464         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15465         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15466         
15467     },
15468     
15469     showTouchView : function()
15470     {
15471         if(this.disabled){
15472             return;
15473         }
15474         
15475         this.touchViewHeaderEl.hide();
15476
15477         if(this.modalTitle.length){
15478             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15479             this.touchViewHeaderEl.show();
15480         }
15481
15482         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15483         this.touchViewEl.show();
15484
15485         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15486         
15487         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15488         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15489
15490         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15491
15492         if(this.modalTitle.length){
15493             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15494         }
15495         
15496         this.touchViewBodyEl.setHeight(bodyHeight);
15497
15498         if(this.animate){
15499             var _this = this;
15500             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15501         }else{
15502             this.touchViewEl.addClass('in');
15503         }
15504         
15505         if(this._touchViewMask){
15506             Roo.get(document.body).addClass("x-body-masked");
15507             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15508             this._touchViewMask.setStyle('z-index', 10000);
15509             this._touchViewMask.addClass('show');
15510         }
15511         
15512         this.doTouchViewQuery();
15513         
15514     },
15515     
15516     hideTouchView : function()
15517     {
15518         this.touchViewEl.removeClass('in');
15519
15520         if(this.animate){
15521             var _this = this;
15522             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15523         }else{
15524             this.touchViewEl.setStyle('display', 'none');
15525         }
15526         
15527         if(this._touchViewMask){
15528             this._touchViewMask.removeClass('show');
15529             Roo.get(document.body).removeClass("x-body-masked");
15530         }
15531     },
15532     
15533     setTouchViewValue : function()
15534     {
15535         if(this.multiple){
15536             this.clearItem();
15537         
15538             var _this = this;
15539
15540             Roo.each(this.tickItems, function(o){
15541                 this.addItem(o);
15542             }, this);
15543         }
15544         
15545         this.hideTouchView();
15546     },
15547     
15548     doTouchViewQuery : function()
15549     {
15550         var qe = {
15551             query: '',
15552             forceAll: true,
15553             combo: this,
15554             cancel:false
15555         };
15556         
15557         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15558             return false;
15559         }
15560         
15561         if(!this.alwaysQuery || this.mode == 'local'){
15562             this.onTouchViewLoad();
15563             return;
15564         }
15565         
15566         this.store.load();
15567     },
15568     
15569     onTouchViewBeforeLoad : function(combo,opts)
15570     {
15571         return;
15572     },
15573
15574     // private
15575     onTouchViewLoad : function()
15576     {
15577         if(this.store.getCount() < 1){
15578             this.onTouchViewEmptyResults();
15579             return;
15580         }
15581         
15582         this.clearTouchView();
15583         
15584         var rawValue = this.getRawValue();
15585         
15586         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15587         
15588         this.tickItems = [];
15589         
15590         this.store.data.each(function(d, rowIndex){
15591             var row = this.touchViewListGroup.createChild(template);
15592             
15593             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15594                 row.addClass(d.data.cls);
15595             }
15596             
15597             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15598                 var cfg = {
15599                     data : d.data,
15600                     html : d.data[this.displayField]
15601                 };
15602                 
15603                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15604                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15605                 }
15606             }
15607             row.removeClass('selected');
15608             if(!this.multiple && this.valueField &&
15609                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15610             {
15611                 // radio buttons..
15612                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15613                 row.addClass('selected');
15614             }
15615             
15616             if(this.multiple && this.valueField &&
15617                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15618             {
15619                 
15620                 // checkboxes...
15621                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15622                 this.tickItems.push(d.data);
15623             }
15624             
15625             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15626             
15627         }, this);
15628         
15629         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15630         
15631         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15632
15633         if(this.modalTitle.length){
15634             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15635         }
15636
15637         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15638         
15639         if(this.mobile_restrict_height && listHeight < bodyHeight){
15640             this.touchViewBodyEl.setHeight(listHeight);
15641         }
15642         
15643         var _this = this;
15644         
15645         if(firstChecked && listHeight > bodyHeight){
15646             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15647         }
15648         
15649     },
15650     
15651     onTouchViewLoadException : function()
15652     {
15653         this.hideTouchView();
15654     },
15655     
15656     onTouchViewEmptyResults : function()
15657     {
15658         this.clearTouchView();
15659         
15660         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15661         
15662         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15663         
15664     },
15665     
15666     clearTouchView : function()
15667     {
15668         this.touchViewListGroup.dom.innerHTML = '';
15669     },
15670     
15671     onTouchViewClick : function(e, el, o)
15672     {
15673         e.preventDefault();
15674         
15675         var row = o.row;
15676         var rowIndex = o.rowIndex;
15677         
15678         var r = this.store.getAt(rowIndex);
15679         
15680         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15681             
15682             if(!this.multiple){
15683                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15684                     c.dom.removeAttribute('checked');
15685                 }, this);
15686
15687                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15688
15689                 this.setFromData(r.data);
15690
15691                 var close = this.closeTriggerEl();
15692
15693                 if(close){
15694                     close.show();
15695                 }
15696
15697                 this.hideTouchView();
15698
15699                 this.fireEvent('select', this, r, rowIndex);
15700
15701                 return;
15702             }
15703
15704             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15705                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15706                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15707                 return;
15708             }
15709
15710             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15711             this.addItem(r.data);
15712             this.tickItems.push(r.data);
15713         }
15714     },
15715     
15716     getAutoCreateNativeIOS : function()
15717     {
15718         var cfg = {
15719             cls: 'form-group' //input-group,
15720         };
15721         
15722         var combobox =  {
15723             tag: 'select',
15724             cls : 'roo-ios-select'
15725         };
15726         
15727         if (this.name) {
15728             combobox.name = this.name;
15729         }
15730         
15731         if (this.disabled) {
15732             combobox.disabled = true;
15733         }
15734         
15735         var settings = this;
15736         
15737         ['xs','sm','md','lg'].map(function(size){
15738             if (settings[size]) {
15739                 cfg.cls += ' col-' + size + '-' + settings[size];
15740             }
15741         });
15742         
15743         cfg.cn = combobox;
15744         
15745         return cfg;
15746         
15747     },
15748     
15749     initIOSView : function()
15750     {
15751         this.store.on('load', this.onIOSViewLoad, this);
15752         
15753         return;
15754     },
15755     
15756     onIOSViewLoad : function()
15757     {
15758         if(this.store.getCount() < 1){
15759             return;
15760         }
15761         
15762         this.clearIOSView();
15763         
15764         if(this.allowBlank) {
15765             
15766             var default_text = '-- SELECT --';
15767             
15768             if(this.placeholder.length){
15769                 default_text = this.placeholder;
15770             }
15771             
15772             if(this.emptyTitle.length){
15773                 default_text += ' - ' + this.emptyTitle + ' -';
15774             }
15775             
15776             var opt = this.inputEl().createChild({
15777                 tag: 'option',
15778                 value : 0,
15779                 html : default_text
15780             });
15781             
15782             var o = {};
15783             o[this.valueField] = 0;
15784             o[this.displayField] = default_text;
15785             
15786             this.ios_options.push({
15787                 data : o,
15788                 el : opt
15789             });
15790             
15791         }
15792         
15793         this.store.data.each(function(d, rowIndex){
15794             
15795             var html = '';
15796             
15797             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15798                 html = d.data[this.displayField];
15799             }
15800             
15801             var value = '';
15802             
15803             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15804                 value = d.data[this.valueField];
15805             }
15806             
15807             var option = {
15808                 tag: 'option',
15809                 value : value,
15810                 html : html
15811             };
15812             
15813             if(this.value == d.data[this.valueField]){
15814                 option['selected'] = true;
15815             }
15816             
15817             var opt = this.inputEl().createChild(option);
15818             
15819             this.ios_options.push({
15820                 data : d.data,
15821                 el : opt
15822             });
15823             
15824         }, this);
15825         
15826         this.inputEl().on('change', function(){
15827            this.fireEvent('select', this);
15828         }, this);
15829         
15830     },
15831     
15832     clearIOSView: function()
15833     {
15834         this.inputEl().dom.innerHTML = '';
15835         
15836         this.ios_options = [];
15837     },
15838     
15839     setIOSValue: function(v)
15840     {
15841         this.value = v;
15842         
15843         if(!this.ios_options){
15844             return;
15845         }
15846         
15847         Roo.each(this.ios_options, function(opts){
15848            
15849            opts.el.dom.removeAttribute('selected');
15850            
15851            if(opts.data[this.valueField] != v){
15852                return;
15853            }
15854            
15855            opts.el.dom.setAttribute('selected', true);
15856            
15857         }, this);
15858     }
15859
15860     /** 
15861     * @cfg {Boolean} grow 
15862     * @hide 
15863     */
15864     /** 
15865     * @cfg {Number} growMin 
15866     * @hide 
15867     */
15868     /** 
15869     * @cfg {Number} growMax 
15870     * @hide 
15871     */
15872     /**
15873      * @hide
15874      * @method autoSize
15875      */
15876 });
15877
15878 Roo.apply(Roo.bootstrap.ComboBox,  {
15879     
15880     header : {
15881         tag: 'div',
15882         cls: 'modal-header',
15883         cn: [
15884             {
15885                 tag: 'h4',
15886                 cls: 'modal-title'
15887             }
15888         ]
15889     },
15890     
15891     body : {
15892         tag: 'div',
15893         cls: 'modal-body',
15894         cn: [
15895             {
15896                 tag: 'ul',
15897                 cls: 'list-group'
15898             }
15899         ]
15900     },
15901     
15902     listItemRadio : {
15903         tag: 'li',
15904         cls: 'list-group-item',
15905         cn: [
15906             {
15907                 tag: 'span',
15908                 cls: 'roo-combobox-list-group-item-value'
15909             },
15910             {
15911                 tag: 'div',
15912                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15913                 cn: [
15914                     {
15915                         tag: 'input',
15916                         type: 'radio'
15917                     },
15918                     {
15919                         tag: 'label'
15920                     }
15921                 ]
15922             }
15923         ]
15924     },
15925     
15926     listItemCheckbox : {
15927         tag: 'li',
15928         cls: 'list-group-item',
15929         cn: [
15930             {
15931                 tag: 'span',
15932                 cls: 'roo-combobox-list-group-item-value'
15933             },
15934             {
15935                 tag: 'div',
15936                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15937                 cn: [
15938                     {
15939                         tag: 'input',
15940                         type: 'checkbox'
15941                     },
15942                     {
15943                         tag: 'label'
15944                     }
15945                 ]
15946             }
15947         ]
15948     },
15949     
15950     emptyResult : {
15951         tag: 'div',
15952         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15953     },
15954     
15955     footer : {
15956         tag: 'div',
15957         cls: 'modal-footer',
15958         cn: [
15959             {
15960                 tag: 'div',
15961                 cls: 'row',
15962                 cn: [
15963                     {
15964                         tag: 'div',
15965                         cls: 'col-xs-6 text-left',
15966                         cn: {
15967                             tag: 'button',
15968                             cls: 'btn btn-danger roo-touch-view-cancel',
15969                             html: 'Cancel'
15970                         }
15971                     },
15972                     {
15973                         tag: 'div',
15974                         cls: 'col-xs-6 text-right',
15975                         cn: {
15976                             tag: 'button',
15977                             cls: 'btn btn-success roo-touch-view-ok',
15978                             html: 'OK'
15979                         }
15980                     }
15981                 ]
15982             }
15983         ]
15984         
15985     }
15986 });
15987
15988 Roo.apply(Roo.bootstrap.ComboBox,  {
15989     
15990     touchViewTemplate : {
15991         tag: 'div',
15992         cls: 'modal fade roo-combobox-touch-view',
15993         cn: [
15994             {
15995                 tag: 'div',
15996                 cls: 'modal-dialog',
15997                 style : 'position:fixed', // we have to fix position....
15998                 cn: [
15999                     {
16000                         tag: 'div',
16001                         cls: 'modal-content',
16002                         cn: [
16003                             Roo.bootstrap.ComboBox.header,
16004                             Roo.bootstrap.ComboBox.body,
16005                             Roo.bootstrap.ComboBox.footer
16006                         ]
16007                     }
16008                 ]
16009             }
16010         ]
16011     }
16012 });/*
16013  * Based on:
16014  * Ext JS Library 1.1.1
16015  * Copyright(c) 2006-2007, Ext JS, LLC.
16016  *
16017  * Originally Released Under LGPL - original licence link has changed is not relivant.
16018  *
16019  * Fork - LGPL
16020  * <script type="text/javascript">
16021  */
16022
16023 /**
16024  * @class Roo.View
16025  * @extends Roo.util.Observable
16026  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16027  * This class also supports single and multi selection modes. <br>
16028  * Create a data model bound view:
16029  <pre><code>
16030  var store = new Roo.data.Store(...);
16031
16032  var view = new Roo.View({
16033     el : "my-element",
16034     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16035  
16036     singleSelect: true,
16037     selectedClass: "ydataview-selected",
16038     store: store
16039  });
16040
16041  // listen for node click?
16042  view.on("click", function(vw, index, node, e){
16043  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16044  });
16045
16046  // load XML data
16047  dataModel.load("foobar.xml");
16048  </code></pre>
16049  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16050  * <br><br>
16051  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16052  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16053  * 
16054  * Note: old style constructor is still suported (container, template, config)
16055  * 
16056  * @constructor
16057  * Create a new View
16058  * @param {Object} config The config object
16059  * 
16060  */
16061 Roo.View = function(config, depreciated_tpl, depreciated_config){
16062     
16063     this.parent = false;
16064     
16065     if (typeof(depreciated_tpl) == 'undefined') {
16066         // new way.. - universal constructor.
16067         Roo.apply(this, config);
16068         this.el  = Roo.get(this.el);
16069     } else {
16070         // old format..
16071         this.el  = Roo.get(config);
16072         this.tpl = depreciated_tpl;
16073         Roo.apply(this, depreciated_config);
16074     }
16075     this.wrapEl  = this.el.wrap().wrap();
16076     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16077     
16078     
16079     if(typeof(this.tpl) == "string"){
16080         this.tpl = new Roo.Template(this.tpl);
16081     } else {
16082         // support xtype ctors..
16083         this.tpl = new Roo.factory(this.tpl, Roo);
16084     }
16085     
16086     
16087     this.tpl.compile();
16088     
16089     /** @private */
16090     this.addEvents({
16091         /**
16092          * @event beforeclick
16093          * Fires before a click is processed. Returns false to cancel the default action.
16094          * @param {Roo.View} this
16095          * @param {Number} index The index of the target node
16096          * @param {HTMLElement} node The target node
16097          * @param {Roo.EventObject} e The raw event object
16098          */
16099             "beforeclick" : true,
16100         /**
16101          * @event click
16102          * Fires when a template node is clicked.
16103          * @param {Roo.View} this
16104          * @param {Number} index The index of the target node
16105          * @param {HTMLElement} node The target node
16106          * @param {Roo.EventObject} e The raw event object
16107          */
16108             "click" : true,
16109         /**
16110          * @event dblclick
16111          * Fires when a template node is double clicked.
16112          * @param {Roo.View} this
16113          * @param {Number} index The index of the target node
16114          * @param {HTMLElement} node The target node
16115          * @param {Roo.EventObject} e The raw event object
16116          */
16117             "dblclick" : true,
16118         /**
16119          * @event contextmenu
16120          * Fires when a template node is right clicked.
16121          * @param {Roo.View} this
16122          * @param {Number} index The index of the target node
16123          * @param {HTMLElement} node The target node
16124          * @param {Roo.EventObject} e The raw event object
16125          */
16126             "contextmenu" : true,
16127         /**
16128          * @event selectionchange
16129          * Fires when the selected nodes change.
16130          * @param {Roo.View} this
16131          * @param {Array} selections Array of the selected nodes
16132          */
16133             "selectionchange" : true,
16134     
16135         /**
16136          * @event beforeselect
16137          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16138          * @param {Roo.View} this
16139          * @param {HTMLElement} node The node to be selected
16140          * @param {Array} selections Array of currently selected nodes
16141          */
16142             "beforeselect" : true,
16143         /**
16144          * @event preparedata
16145          * Fires on every row to render, to allow you to change the data.
16146          * @param {Roo.View} this
16147          * @param {Object} data to be rendered (change this)
16148          */
16149           "preparedata" : true
16150           
16151           
16152         });
16153
16154
16155
16156     this.el.on({
16157         "click": this.onClick,
16158         "dblclick": this.onDblClick,
16159         "contextmenu": this.onContextMenu,
16160         scope:this
16161     });
16162
16163     this.selections = [];
16164     this.nodes = [];
16165     this.cmp = new Roo.CompositeElementLite([]);
16166     if(this.store){
16167         this.store = Roo.factory(this.store, Roo.data);
16168         this.setStore(this.store, true);
16169     }
16170     
16171     if ( this.footer && this.footer.xtype) {
16172            
16173          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16174         
16175         this.footer.dataSource = this.store;
16176         this.footer.container = fctr;
16177         this.footer = Roo.factory(this.footer, Roo);
16178         fctr.insertFirst(this.el);
16179         
16180         // this is a bit insane - as the paging toolbar seems to detach the el..
16181 //        dom.parentNode.parentNode.parentNode
16182          // they get detached?
16183     }
16184     
16185     
16186     Roo.View.superclass.constructor.call(this);
16187     
16188     
16189 };
16190
16191 Roo.extend(Roo.View, Roo.util.Observable, {
16192     
16193      /**
16194      * @cfg {Roo.data.Store} store Data store to load data from.
16195      */
16196     store : false,
16197     
16198     /**
16199      * @cfg {String|Roo.Element} el The container element.
16200      */
16201     el : '',
16202     
16203     /**
16204      * @cfg {String|Roo.Template} tpl The template used by this View 
16205      */
16206     tpl : false,
16207     /**
16208      * @cfg {String} dataName the named area of the template to use as the data area
16209      *                          Works with domtemplates roo-name="name"
16210      */
16211     dataName: false,
16212     /**
16213      * @cfg {String} selectedClass The css class to add to selected nodes
16214      */
16215     selectedClass : "x-view-selected",
16216      /**
16217      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16218      */
16219     emptyText : "",
16220     
16221     /**
16222      * @cfg {String} text to display on mask (default Loading)
16223      */
16224     mask : false,
16225     /**
16226      * @cfg {Boolean} multiSelect Allow multiple selection
16227      */
16228     multiSelect : false,
16229     /**
16230      * @cfg {Boolean} singleSelect Allow single selection
16231      */
16232     singleSelect:  false,
16233     
16234     /**
16235      * @cfg {Boolean} toggleSelect - selecting 
16236      */
16237     toggleSelect : false,
16238     
16239     /**
16240      * @cfg {Boolean} tickable - selecting 
16241      */
16242     tickable : false,
16243     
16244     /**
16245      * Returns the element this view is bound to.
16246      * @return {Roo.Element}
16247      */
16248     getEl : function(){
16249         return this.wrapEl;
16250     },
16251     
16252     
16253
16254     /**
16255      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16256      */
16257     refresh : function(){
16258         //Roo.log('refresh');
16259         var t = this.tpl;
16260         
16261         // if we are using something like 'domtemplate', then
16262         // the what gets used is:
16263         // t.applySubtemplate(NAME, data, wrapping data..)
16264         // the outer template then get' applied with
16265         //     the store 'extra data'
16266         // and the body get's added to the
16267         //      roo-name="data" node?
16268         //      <span class='roo-tpl-{name}'></span> ?????
16269         
16270         
16271         
16272         this.clearSelections();
16273         this.el.update("");
16274         var html = [];
16275         var records = this.store.getRange();
16276         if(records.length < 1) {
16277             
16278             // is this valid??  = should it render a template??
16279             
16280             this.el.update(this.emptyText);
16281             return;
16282         }
16283         var el = this.el;
16284         if (this.dataName) {
16285             this.el.update(t.apply(this.store.meta)); //????
16286             el = this.el.child('.roo-tpl-' + this.dataName);
16287         }
16288         
16289         for(var i = 0, len = records.length; i < len; i++){
16290             var data = this.prepareData(records[i].data, i, records[i]);
16291             this.fireEvent("preparedata", this, data, i, records[i]);
16292             
16293             var d = Roo.apply({}, data);
16294             
16295             if(this.tickable){
16296                 Roo.apply(d, {'roo-id' : Roo.id()});
16297                 
16298                 var _this = this;
16299             
16300                 Roo.each(this.parent.item, function(item){
16301                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16302                         return;
16303                     }
16304                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16305                 });
16306             }
16307             
16308             html[html.length] = Roo.util.Format.trim(
16309                 this.dataName ?
16310                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16311                     t.apply(d)
16312             );
16313         }
16314         
16315         
16316         
16317         el.update(html.join(""));
16318         this.nodes = el.dom.childNodes;
16319         this.updateIndexes(0);
16320     },
16321     
16322
16323     /**
16324      * Function to override to reformat the data that is sent to
16325      * the template for each node.
16326      * DEPRICATED - use the preparedata event handler.
16327      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16328      * a JSON object for an UpdateManager bound view).
16329      */
16330     prepareData : function(data, index, record)
16331     {
16332         this.fireEvent("preparedata", this, data, index, record);
16333         return data;
16334     },
16335
16336     onUpdate : function(ds, record){
16337         // Roo.log('on update');   
16338         this.clearSelections();
16339         var index = this.store.indexOf(record);
16340         var n = this.nodes[index];
16341         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16342         n.parentNode.removeChild(n);
16343         this.updateIndexes(index, index);
16344     },
16345
16346     
16347     
16348 // --------- FIXME     
16349     onAdd : function(ds, records, index)
16350     {
16351         //Roo.log(['on Add', ds, records, index] );        
16352         this.clearSelections();
16353         if(this.nodes.length == 0){
16354             this.refresh();
16355             return;
16356         }
16357         var n = this.nodes[index];
16358         for(var i = 0, len = records.length; i < len; i++){
16359             var d = this.prepareData(records[i].data, i, records[i]);
16360             if(n){
16361                 this.tpl.insertBefore(n, d);
16362             }else{
16363                 
16364                 this.tpl.append(this.el, d);
16365             }
16366         }
16367         this.updateIndexes(index);
16368     },
16369
16370     onRemove : function(ds, record, index){
16371        // Roo.log('onRemove');
16372         this.clearSelections();
16373         var el = this.dataName  ?
16374             this.el.child('.roo-tpl-' + this.dataName) :
16375             this.el; 
16376         
16377         el.dom.removeChild(this.nodes[index]);
16378         this.updateIndexes(index);
16379     },
16380
16381     /**
16382      * Refresh an individual node.
16383      * @param {Number} index
16384      */
16385     refreshNode : function(index){
16386         this.onUpdate(this.store, this.store.getAt(index));
16387     },
16388
16389     updateIndexes : function(startIndex, endIndex){
16390         var ns = this.nodes;
16391         startIndex = startIndex || 0;
16392         endIndex = endIndex || ns.length - 1;
16393         for(var i = startIndex; i <= endIndex; i++){
16394             ns[i].nodeIndex = i;
16395         }
16396     },
16397
16398     /**
16399      * Changes the data store this view uses and refresh the view.
16400      * @param {Store} store
16401      */
16402     setStore : function(store, initial){
16403         if(!initial && this.store){
16404             this.store.un("datachanged", this.refresh);
16405             this.store.un("add", this.onAdd);
16406             this.store.un("remove", this.onRemove);
16407             this.store.un("update", this.onUpdate);
16408             this.store.un("clear", this.refresh);
16409             this.store.un("beforeload", this.onBeforeLoad);
16410             this.store.un("load", this.onLoad);
16411             this.store.un("loadexception", this.onLoad);
16412         }
16413         if(store){
16414           
16415             store.on("datachanged", this.refresh, this);
16416             store.on("add", this.onAdd, this);
16417             store.on("remove", this.onRemove, this);
16418             store.on("update", this.onUpdate, this);
16419             store.on("clear", this.refresh, this);
16420             store.on("beforeload", this.onBeforeLoad, this);
16421             store.on("load", this.onLoad, this);
16422             store.on("loadexception", this.onLoad, this);
16423         }
16424         
16425         if(store){
16426             this.refresh();
16427         }
16428     },
16429     /**
16430      * onbeforeLoad - masks the loading area.
16431      *
16432      */
16433     onBeforeLoad : function(store,opts)
16434     {
16435          //Roo.log('onBeforeLoad');   
16436         if (!opts.add) {
16437             this.el.update("");
16438         }
16439         this.el.mask(this.mask ? this.mask : "Loading" ); 
16440     },
16441     onLoad : function ()
16442     {
16443         this.el.unmask();
16444     },
16445     
16446
16447     /**
16448      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16449      * @param {HTMLElement} node
16450      * @return {HTMLElement} The template node
16451      */
16452     findItemFromChild : function(node){
16453         var el = this.dataName  ?
16454             this.el.child('.roo-tpl-' + this.dataName,true) :
16455             this.el.dom; 
16456         
16457         if(!node || node.parentNode == el){
16458                     return node;
16459             }
16460             var p = node.parentNode;
16461             while(p && p != el){
16462             if(p.parentNode == el){
16463                 return p;
16464             }
16465             p = p.parentNode;
16466         }
16467             return null;
16468     },
16469
16470     /** @ignore */
16471     onClick : function(e){
16472         var item = this.findItemFromChild(e.getTarget());
16473         if(item){
16474             var index = this.indexOf(item);
16475             if(this.onItemClick(item, index, e) !== false){
16476                 this.fireEvent("click", this, index, item, e);
16477             }
16478         }else{
16479             this.clearSelections();
16480         }
16481     },
16482
16483     /** @ignore */
16484     onContextMenu : function(e){
16485         var item = this.findItemFromChild(e.getTarget());
16486         if(item){
16487             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16488         }
16489     },
16490
16491     /** @ignore */
16492     onDblClick : function(e){
16493         var item = this.findItemFromChild(e.getTarget());
16494         if(item){
16495             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16496         }
16497     },
16498
16499     onItemClick : function(item, index, e)
16500     {
16501         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16502             return false;
16503         }
16504         if (this.toggleSelect) {
16505             var m = this.isSelected(item) ? 'unselect' : 'select';
16506             //Roo.log(m);
16507             var _t = this;
16508             _t[m](item, true, false);
16509             return true;
16510         }
16511         if(this.multiSelect || this.singleSelect){
16512             if(this.multiSelect && e.shiftKey && this.lastSelection){
16513                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16514             }else{
16515                 this.select(item, this.multiSelect && e.ctrlKey);
16516                 this.lastSelection = item;
16517             }
16518             
16519             if(!this.tickable){
16520                 e.preventDefault();
16521             }
16522             
16523         }
16524         return true;
16525     },
16526
16527     /**
16528      * Get the number of selected nodes.
16529      * @return {Number}
16530      */
16531     getSelectionCount : function(){
16532         return this.selections.length;
16533     },
16534
16535     /**
16536      * Get the currently selected nodes.
16537      * @return {Array} An array of HTMLElements
16538      */
16539     getSelectedNodes : function(){
16540         return this.selections;
16541     },
16542
16543     /**
16544      * Get the indexes of the selected nodes.
16545      * @return {Array}
16546      */
16547     getSelectedIndexes : function(){
16548         var indexes = [], s = this.selections;
16549         for(var i = 0, len = s.length; i < len; i++){
16550             indexes.push(s[i].nodeIndex);
16551         }
16552         return indexes;
16553     },
16554
16555     /**
16556      * Clear all selections
16557      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16558      */
16559     clearSelections : function(suppressEvent){
16560         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16561             this.cmp.elements = this.selections;
16562             this.cmp.removeClass(this.selectedClass);
16563             this.selections = [];
16564             if(!suppressEvent){
16565                 this.fireEvent("selectionchange", this, this.selections);
16566             }
16567         }
16568     },
16569
16570     /**
16571      * Returns true if the passed node is selected
16572      * @param {HTMLElement/Number} node The node or node index
16573      * @return {Boolean}
16574      */
16575     isSelected : function(node){
16576         var s = this.selections;
16577         if(s.length < 1){
16578             return false;
16579         }
16580         node = this.getNode(node);
16581         return s.indexOf(node) !== -1;
16582     },
16583
16584     /**
16585      * Selects nodes.
16586      * @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
16587      * @param {Boolean} keepExisting (optional) true to keep existing selections
16588      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16589      */
16590     select : function(nodeInfo, keepExisting, suppressEvent){
16591         if(nodeInfo instanceof Array){
16592             if(!keepExisting){
16593                 this.clearSelections(true);
16594             }
16595             for(var i = 0, len = nodeInfo.length; i < len; i++){
16596                 this.select(nodeInfo[i], true, true);
16597             }
16598             return;
16599         } 
16600         var node = this.getNode(nodeInfo);
16601         if(!node || this.isSelected(node)){
16602             return; // already selected.
16603         }
16604         if(!keepExisting){
16605             this.clearSelections(true);
16606         }
16607         
16608         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16609             Roo.fly(node).addClass(this.selectedClass);
16610             this.selections.push(node);
16611             if(!suppressEvent){
16612                 this.fireEvent("selectionchange", this, this.selections);
16613             }
16614         }
16615         
16616         
16617     },
16618       /**
16619      * Unselects nodes.
16620      * @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
16621      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16623      */
16624     unselect : function(nodeInfo, keepExisting, suppressEvent)
16625     {
16626         if(nodeInfo instanceof Array){
16627             Roo.each(this.selections, function(s) {
16628                 this.unselect(s, nodeInfo);
16629             }, this);
16630             return;
16631         }
16632         var node = this.getNode(nodeInfo);
16633         if(!node || !this.isSelected(node)){
16634             //Roo.log("not selected");
16635             return; // not selected.
16636         }
16637         // fireevent???
16638         var ns = [];
16639         Roo.each(this.selections, function(s) {
16640             if (s == node ) {
16641                 Roo.fly(node).removeClass(this.selectedClass);
16642
16643                 return;
16644             }
16645             ns.push(s);
16646         },this);
16647         
16648         this.selections= ns;
16649         this.fireEvent("selectionchange", this, this.selections);
16650     },
16651
16652     /**
16653      * Gets a template node.
16654      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16655      * @return {HTMLElement} The node or null if it wasn't found
16656      */
16657     getNode : function(nodeInfo){
16658         if(typeof nodeInfo == "string"){
16659             return document.getElementById(nodeInfo);
16660         }else if(typeof nodeInfo == "number"){
16661             return this.nodes[nodeInfo];
16662         }
16663         return nodeInfo;
16664     },
16665
16666     /**
16667      * Gets a range template nodes.
16668      * @param {Number} startIndex
16669      * @param {Number} endIndex
16670      * @return {Array} An array of nodes
16671      */
16672     getNodes : function(start, end){
16673         var ns = this.nodes;
16674         start = start || 0;
16675         end = typeof end == "undefined" ? ns.length - 1 : end;
16676         var nodes = [];
16677         if(start <= end){
16678             for(var i = start; i <= end; i++){
16679                 nodes.push(ns[i]);
16680             }
16681         } else{
16682             for(var i = start; i >= end; i--){
16683                 nodes.push(ns[i]);
16684             }
16685         }
16686         return nodes;
16687     },
16688
16689     /**
16690      * Finds the index of the passed node
16691      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16692      * @return {Number} The index of the node or -1
16693      */
16694     indexOf : function(node){
16695         node = this.getNode(node);
16696         if(typeof node.nodeIndex == "number"){
16697             return node.nodeIndex;
16698         }
16699         var ns = this.nodes;
16700         for(var i = 0, len = ns.length; i < len; i++){
16701             if(ns[i] == node){
16702                 return i;
16703             }
16704         }
16705         return -1;
16706     }
16707 });
16708 /*
16709  * - LGPL
16710  *
16711  * based on jquery fullcalendar
16712  * 
16713  */
16714
16715 Roo.bootstrap = Roo.bootstrap || {};
16716 /**
16717  * @class Roo.bootstrap.Calendar
16718  * @extends Roo.bootstrap.Component
16719  * Bootstrap Calendar class
16720  * @cfg {Boolean} loadMask (true|false) default false
16721  * @cfg {Object} header generate the user specific header of the calendar, default false
16722
16723  * @constructor
16724  * Create a new Container
16725  * @param {Object} config The config object
16726  */
16727
16728
16729
16730 Roo.bootstrap.Calendar = function(config){
16731     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16732      this.addEvents({
16733         /**
16734              * @event select
16735              * Fires when a date is selected
16736              * @param {DatePicker} this
16737              * @param {Date} date The selected date
16738              */
16739         'select': true,
16740         /**
16741              * @event monthchange
16742              * Fires when the displayed month changes 
16743              * @param {DatePicker} this
16744              * @param {Date} date The selected month
16745              */
16746         'monthchange': true,
16747         /**
16748              * @event evententer
16749              * Fires when mouse over an event
16750              * @param {Calendar} this
16751              * @param {event} Event
16752              */
16753         'evententer': true,
16754         /**
16755              * @event eventleave
16756              * Fires when the mouse leaves an
16757              * @param {Calendar} this
16758              * @param {event}
16759              */
16760         'eventleave': true,
16761         /**
16762              * @event eventclick
16763              * Fires when the mouse click an
16764              * @param {Calendar} this
16765              * @param {event}
16766              */
16767         'eventclick': true
16768         
16769     });
16770
16771 };
16772
16773 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16774     
16775      /**
16776      * @cfg {Number} startDay
16777      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16778      */
16779     startDay : 0,
16780     
16781     loadMask : false,
16782     
16783     header : false,
16784       
16785     getAutoCreate : function(){
16786         
16787         
16788         var fc_button = function(name, corner, style, content ) {
16789             return Roo.apply({},{
16790                 tag : 'span',
16791                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16792                          (corner.length ?
16793                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16794                             ''
16795                         ),
16796                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16797                 unselectable: 'on'
16798             });
16799         };
16800         
16801         var header = {};
16802         
16803         if(!this.header){
16804             header = {
16805                 tag : 'table',
16806                 cls : 'fc-header',
16807                 style : 'width:100%',
16808                 cn : [
16809                     {
16810                         tag: 'tr',
16811                         cn : [
16812                             {
16813                                 tag : 'td',
16814                                 cls : 'fc-header-left',
16815                                 cn : [
16816                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16817                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16818                                     { tag: 'span', cls: 'fc-header-space' },
16819                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16820
16821
16822                                 ]
16823                             },
16824
16825                             {
16826                                 tag : 'td',
16827                                 cls : 'fc-header-center',
16828                                 cn : [
16829                                     {
16830                                         tag: 'span',
16831                                         cls: 'fc-header-title',
16832                                         cn : {
16833                                             tag: 'H2',
16834                                             html : 'month / year'
16835                                         }
16836                                     }
16837
16838                                 ]
16839                             },
16840                             {
16841                                 tag : 'td',
16842                                 cls : 'fc-header-right',
16843                                 cn : [
16844                               /*      fc_button('month', 'left', '', 'month' ),
16845                                     fc_button('week', '', '', 'week' ),
16846                                     fc_button('day', 'right', '', 'day' )
16847                                 */    
16848
16849                                 ]
16850                             }
16851
16852                         ]
16853                     }
16854                 ]
16855             };
16856         }
16857         
16858         header = this.header;
16859         
16860        
16861         var cal_heads = function() {
16862             var ret = [];
16863             // fixme - handle this.
16864             
16865             for (var i =0; i < Date.dayNames.length; i++) {
16866                 var d = Date.dayNames[i];
16867                 ret.push({
16868                     tag: 'th',
16869                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16870                     html : d.substring(0,3)
16871                 });
16872                 
16873             }
16874             ret[0].cls += ' fc-first';
16875             ret[6].cls += ' fc-last';
16876             return ret;
16877         };
16878         var cal_cell = function(n) {
16879             return  {
16880                 tag: 'td',
16881                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16882                 cn : [
16883                     {
16884                         cn : [
16885                             {
16886                                 cls: 'fc-day-number',
16887                                 html: 'D'
16888                             },
16889                             {
16890                                 cls: 'fc-day-content',
16891                              
16892                                 cn : [
16893                                      {
16894                                         style: 'position: relative;' // height: 17px;
16895                                     }
16896                                 ]
16897                             }
16898                             
16899                             
16900                         ]
16901                     }
16902                 ]
16903                 
16904             }
16905         };
16906         var cal_rows = function() {
16907             
16908             var ret = [];
16909             for (var r = 0; r < 6; r++) {
16910                 var row= {
16911                     tag : 'tr',
16912                     cls : 'fc-week',
16913                     cn : []
16914                 };
16915                 
16916                 for (var i =0; i < Date.dayNames.length; i++) {
16917                     var d = Date.dayNames[i];
16918                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16919
16920                 }
16921                 row.cn[0].cls+=' fc-first';
16922                 row.cn[0].cn[0].style = 'min-height:90px';
16923                 row.cn[6].cls+=' fc-last';
16924                 ret.push(row);
16925                 
16926             }
16927             ret[0].cls += ' fc-first';
16928             ret[4].cls += ' fc-prev-last';
16929             ret[5].cls += ' fc-last';
16930             return ret;
16931             
16932         };
16933         
16934         var cal_table = {
16935             tag: 'table',
16936             cls: 'fc-border-separate',
16937             style : 'width:100%',
16938             cellspacing  : 0,
16939             cn : [
16940                 { 
16941                     tag: 'thead',
16942                     cn : [
16943                         { 
16944                             tag: 'tr',
16945                             cls : 'fc-first fc-last',
16946                             cn : cal_heads()
16947                         }
16948                     ]
16949                 },
16950                 { 
16951                     tag: 'tbody',
16952                     cn : cal_rows()
16953                 }
16954                   
16955             ]
16956         };
16957          
16958          var cfg = {
16959             cls : 'fc fc-ltr',
16960             cn : [
16961                 header,
16962                 {
16963                     cls : 'fc-content',
16964                     style : "position: relative;",
16965                     cn : [
16966                         {
16967                             cls : 'fc-view fc-view-month fc-grid',
16968                             style : 'position: relative',
16969                             unselectable : 'on',
16970                             cn : [
16971                                 {
16972                                     cls : 'fc-event-container',
16973                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16974                                 },
16975                                 cal_table
16976                             ]
16977                         }
16978                     ]
16979     
16980                 }
16981            ] 
16982             
16983         };
16984         
16985          
16986         
16987         return cfg;
16988     },
16989     
16990     
16991     initEvents : function()
16992     {
16993         if(!this.store){
16994             throw "can not find store for calendar";
16995         }
16996         
16997         var mark = {
16998             tag: "div",
16999             cls:"x-dlg-mask",
17000             style: "text-align:center",
17001             cn: [
17002                 {
17003                     tag: "div",
17004                     style: "background-color:white;width:50%;margin:250 auto",
17005                     cn: [
17006                         {
17007                             tag: "img",
17008                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17009                         },
17010                         {
17011                             tag: "span",
17012                             html: "Loading"
17013                         }
17014                         
17015                     ]
17016                 }
17017             ]
17018         };
17019         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17020         
17021         var size = this.el.select('.fc-content', true).first().getSize();
17022         this.maskEl.setSize(size.width, size.height);
17023         this.maskEl.enableDisplayMode("block");
17024         if(!this.loadMask){
17025             this.maskEl.hide();
17026         }
17027         
17028         this.store = Roo.factory(this.store, Roo.data);
17029         this.store.on('load', this.onLoad, this);
17030         this.store.on('beforeload', this.onBeforeLoad, this);
17031         
17032         this.resize();
17033         
17034         this.cells = this.el.select('.fc-day',true);
17035         //Roo.log(this.cells);
17036         this.textNodes = this.el.query('.fc-day-number');
17037         this.cells.addClassOnOver('fc-state-hover');
17038         
17039         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17040         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17041         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17042         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17043         
17044         this.on('monthchange', this.onMonthChange, this);
17045         
17046         this.update(new Date().clearTime());
17047     },
17048     
17049     resize : function() {
17050         var sz  = this.el.getSize();
17051         
17052         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17053         this.el.select('.fc-day-content div',true).setHeight(34);
17054     },
17055     
17056     
17057     // private
17058     showPrevMonth : function(e){
17059         this.update(this.activeDate.add("mo", -1));
17060     },
17061     showToday : function(e){
17062         this.update(new Date().clearTime());
17063     },
17064     // private
17065     showNextMonth : function(e){
17066         this.update(this.activeDate.add("mo", 1));
17067     },
17068
17069     // private
17070     showPrevYear : function(){
17071         this.update(this.activeDate.add("y", -1));
17072     },
17073
17074     // private
17075     showNextYear : function(){
17076         this.update(this.activeDate.add("y", 1));
17077     },
17078
17079     
17080    // private
17081     update : function(date)
17082     {
17083         var vd = this.activeDate;
17084         this.activeDate = date;
17085 //        if(vd && this.el){
17086 //            var t = date.getTime();
17087 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17088 //                Roo.log('using add remove');
17089 //                
17090 //                this.fireEvent('monthchange', this, date);
17091 //                
17092 //                this.cells.removeClass("fc-state-highlight");
17093 //                this.cells.each(function(c){
17094 //                   if(c.dateValue == t){
17095 //                       c.addClass("fc-state-highlight");
17096 //                       setTimeout(function(){
17097 //                            try{c.dom.firstChild.focus();}catch(e){}
17098 //                       }, 50);
17099 //                       return false;
17100 //                   }
17101 //                   return true;
17102 //                });
17103 //                return;
17104 //            }
17105 //        }
17106         
17107         var days = date.getDaysInMonth();
17108         
17109         var firstOfMonth = date.getFirstDateOfMonth();
17110         var startingPos = firstOfMonth.getDay()-this.startDay;
17111         
17112         if(startingPos < this.startDay){
17113             startingPos += 7;
17114         }
17115         
17116         var pm = date.add(Date.MONTH, -1);
17117         var prevStart = pm.getDaysInMonth()-startingPos;
17118 //        
17119         this.cells = this.el.select('.fc-day',true);
17120         this.textNodes = this.el.query('.fc-day-number');
17121         this.cells.addClassOnOver('fc-state-hover');
17122         
17123         var cells = this.cells.elements;
17124         var textEls = this.textNodes;
17125         
17126         Roo.each(cells, function(cell){
17127             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17128         });
17129         
17130         days += startingPos;
17131
17132         // convert everything to numbers so it's fast
17133         var day = 86400000;
17134         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17135         //Roo.log(d);
17136         //Roo.log(pm);
17137         //Roo.log(prevStart);
17138         
17139         var today = new Date().clearTime().getTime();
17140         var sel = date.clearTime().getTime();
17141         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17142         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17143         var ddMatch = this.disabledDatesRE;
17144         var ddText = this.disabledDatesText;
17145         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17146         var ddaysText = this.disabledDaysText;
17147         var format = this.format;
17148         
17149         var setCellClass = function(cal, cell){
17150             cell.row = 0;
17151             cell.events = [];
17152             cell.more = [];
17153             //Roo.log('set Cell Class');
17154             cell.title = "";
17155             var t = d.getTime();
17156             
17157             //Roo.log(d);
17158             
17159             cell.dateValue = t;
17160             if(t == today){
17161                 cell.className += " fc-today";
17162                 cell.className += " fc-state-highlight";
17163                 cell.title = cal.todayText;
17164             }
17165             if(t == sel){
17166                 // disable highlight in other month..
17167                 //cell.className += " fc-state-highlight";
17168                 
17169             }
17170             // disabling
17171             if(t < min) {
17172                 cell.className = " fc-state-disabled";
17173                 cell.title = cal.minText;
17174                 return;
17175             }
17176             if(t > max) {
17177                 cell.className = " fc-state-disabled";
17178                 cell.title = cal.maxText;
17179                 return;
17180             }
17181             if(ddays){
17182                 if(ddays.indexOf(d.getDay()) != -1){
17183                     cell.title = ddaysText;
17184                     cell.className = " fc-state-disabled";
17185                 }
17186             }
17187             if(ddMatch && format){
17188                 var fvalue = d.dateFormat(format);
17189                 if(ddMatch.test(fvalue)){
17190                     cell.title = ddText.replace("%0", fvalue);
17191                     cell.className = " fc-state-disabled";
17192                 }
17193             }
17194             
17195             if (!cell.initialClassName) {
17196                 cell.initialClassName = cell.dom.className;
17197             }
17198             
17199             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17200         };
17201
17202         var i = 0;
17203         
17204         for(; i < startingPos; i++) {
17205             textEls[i].innerHTML = (++prevStart);
17206             d.setDate(d.getDate()+1);
17207             
17208             cells[i].className = "fc-past fc-other-month";
17209             setCellClass(this, cells[i]);
17210         }
17211         
17212         var intDay = 0;
17213         
17214         for(; i < days; i++){
17215             intDay = i - startingPos + 1;
17216             textEls[i].innerHTML = (intDay);
17217             d.setDate(d.getDate()+1);
17218             
17219             cells[i].className = ''; // "x-date-active";
17220             setCellClass(this, cells[i]);
17221         }
17222         var extraDays = 0;
17223         
17224         for(; i < 42; i++) {
17225             textEls[i].innerHTML = (++extraDays);
17226             d.setDate(d.getDate()+1);
17227             
17228             cells[i].className = "fc-future fc-other-month";
17229             setCellClass(this, cells[i]);
17230         }
17231         
17232         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17233         
17234         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17235         
17236         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17237         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17238         
17239         if(totalRows != 6){
17240             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17241             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17242         }
17243         
17244         this.fireEvent('monthchange', this, date);
17245         
17246         
17247         /*
17248         if(!this.internalRender){
17249             var main = this.el.dom.firstChild;
17250             var w = main.offsetWidth;
17251             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17252             Roo.fly(main).setWidth(w);
17253             this.internalRender = true;
17254             // opera does not respect the auto grow header center column
17255             // then, after it gets a width opera refuses to recalculate
17256             // without a second pass
17257             if(Roo.isOpera && !this.secondPass){
17258                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17259                 this.secondPass = true;
17260                 this.update.defer(10, this, [date]);
17261             }
17262         }
17263         */
17264         
17265     },
17266     
17267     findCell : function(dt) {
17268         dt = dt.clearTime().getTime();
17269         var ret = false;
17270         this.cells.each(function(c){
17271             //Roo.log("check " +c.dateValue + '?=' + dt);
17272             if(c.dateValue == dt){
17273                 ret = c;
17274                 return false;
17275             }
17276             return true;
17277         });
17278         
17279         return ret;
17280     },
17281     
17282     findCells : function(ev) {
17283         var s = ev.start.clone().clearTime().getTime();
17284        // Roo.log(s);
17285         var e= ev.end.clone().clearTime().getTime();
17286        // Roo.log(e);
17287         var ret = [];
17288         this.cells.each(function(c){
17289              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17290             
17291             if(c.dateValue > e){
17292                 return ;
17293             }
17294             if(c.dateValue < s){
17295                 return ;
17296             }
17297             ret.push(c);
17298         });
17299         
17300         return ret;    
17301     },
17302     
17303 //    findBestRow: function(cells)
17304 //    {
17305 //        var ret = 0;
17306 //        
17307 //        for (var i =0 ; i < cells.length;i++) {
17308 //            ret  = Math.max(cells[i].rows || 0,ret);
17309 //        }
17310 //        return ret;
17311 //        
17312 //    },
17313     
17314     
17315     addItem : function(ev)
17316     {
17317         // look for vertical location slot in
17318         var cells = this.findCells(ev);
17319         
17320 //        ev.row = this.findBestRow(cells);
17321         
17322         // work out the location.
17323         
17324         var crow = false;
17325         var rows = [];
17326         for(var i =0; i < cells.length; i++) {
17327             
17328             cells[i].row = cells[0].row;
17329             
17330             if(i == 0){
17331                 cells[i].row = cells[i].row + 1;
17332             }
17333             
17334             if (!crow) {
17335                 crow = {
17336                     start : cells[i],
17337                     end :  cells[i]
17338                 };
17339                 continue;
17340             }
17341             if (crow.start.getY() == cells[i].getY()) {
17342                 // on same row.
17343                 crow.end = cells[i];
17344                 continue;
17345             }
17346             // different row.
17347             rows.push(crow);
17348             crow = {
17349                 start: cells[i],
17350                 end : cells[i]
17351             };
17352             
17353         }
17354         
17355         rows.push(crow);
17356         ev.els = [];
17357         ev.rows = rows;
17358         ev.cells = cells;
17359         
17360         cells[0].events.push(ev);
17361         
17362         this.calevents.push(ev);
17363     },
17364     
17365     clearEvents: function() {
17366         
17367         if(!this.calevents){
17368             return;
17369         }
17370         
17371         Roo.each(this.cells.elements, function(c){
17372             c.row = 0;
17373             c.events = [];
17374             c.more = [];
17375         });
17376         
17377         Roo.each(this.calevents, function(e) {
17378             Roo.each(e.els, function(el) {
17379                 el.un('mouseenter' ,this.onEventEnter, this);
17380                 el.un('mouseleave' ,this.onEventLeave, this);
17381                 el.remove();
17382             },this);
17383         },this);
17384         
17385         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17386             e.remove();
17387         });
17388         
17389     },
17390     
17391     renderEvents: function()
17392     {   
17393         var _this = this;
17394         
17395         this.cells.each(function(c) {
17396             
17397             if(c.row < 5){
17398                 return;
17399             }
17400             
17401             var ev = c.events;
17402             
17403             var r = 4;
17404             if(c.row != c.events.length){
17405                 r = 4 - (4 - (c.row - c.events.length));
17406             }
17407             
17408             c.events = ev.slice(0, r);
17409             c.more = ev.slice(r);
17410             
17411             if(c.more.length && c.more.length == 1){
17412                 c.events.push(c.more.pop());
17413             }
17414             
17415             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17416             
17417         });
17418             
17419         this.cells.each(function(c) {
17420             
17421             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17422             
17423             
17424             for (var e = 0; e < c.events.length; e++){
17425                 var ev = c.events[e];
17426                 var rows = ev.rows;
17427                 
17428                 for(var i = 0; i < rows.length; i++) {
17429                 
17430                     // how many rows should it span..
17431
17432                     var  cfg = {
17433                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17434                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17435
17436                         unselectable : "on",
17437                         cn : [
17438                             {
17439                                 cls: 'fc-event-inner',
17440                                 cn : [
17441     //                                {
17442     //                                  tag:'span',
17443     //                                  cls: 'fc-event-time',
17444     //                                  html : cells.length > 1 ? '' : ev.time
17445     //                                },
17446                                     {
17447                                       tag:'span',
17448                                       cls: 'fc-event-title',
17449                                       html : String.format('{0}', ev.title)
17450                                     }
17451
17452
17453                                 ]
17454                             },
17455                             {
17456                                 cls: 'ui-resizable-handle ui-resizable-e',
17457                                 html : '&nbsp;&nbsp;&nbsp'
17458                             }
17459
17460                         ]
17461                     };
17462
17463                     if (i == 0) {
17464                         cfg.cls += ' fc-event-start';
17465                     }
17466                     if ((i+1) == rows.length) {
17467                         cfg.cls += ' fc-event-end';
17468                     }
17469
17470                     var ctr = _this.el.select('.fc-event-container',true).first();
17471                     var cg = ctr.createChild(cfg);
17472
17473                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17474                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17475
17476                     var r = (c.more.length) ? 1 : 0;
17477                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17478                     cg.setWidth(ebox.right - sbox.x -2);
17479
17480                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17481                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17482                     cg.on('click', _this.onEventClick, _this, ev);
17483
17484                     ev.els.push(cg);
17485                     
17486                 }
17487                 
17488             }
17489             
17490             
17491             if(c.more.length){
17492                 var  cfg = {
17493                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17494                     style : 'position: absolute',
17495                     unselectable : "on",
17496                     cn : [
17497                         {
17498                             cls: 'fc-event-inner',
17499                             cn : [
17500                                 {
17501                                   tag:'span',
17502                                   cls: 'fc-event-title',
17503                                   html : 'More'
17504                                 }
17505
17506
17507                             ]
17508                         },
17509                         {
17510                             cls: 'ui-resizable-handle ui-resizable-e',
17511                             html : '&nbsp;&nbsp;&nbsp'
17512                         }
17513
17514                     ]
17515                 };
17516
17517                 var ctr = _this.el.select('.fc-event-container',true).first();
17518                 var cg = ctr.createChild(cfg);
17519
17520                 var sbox = c.select('.fc-day-content',true).first().getBox();
17521                 var ebox = c.select('.fc-day-content',true).first().getBox();
17522                 //Roo.log(cg);
17523                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17524                 cg.setWidth(ebox.right - sbox.x -2);
17525
17526                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17527                 
17528             }
17529             
17530         });
17531         
17532         
17533         
17534     },
17535     
17536     onEventEnter: function (e, el,event,d) {
17537         this.fireEvent('evententer', this, el, event);
17538     },
17539     
17540     onEventLeave: function (e, el,event,d) {
17541         this.fireEvent('eventleave', this, el, event);
17542     },
17543     
17544     onEventClick: function (e, el,event,d) {
17545         this.fireEvent('eventclick', this, el, event);
17546     },
17547     
17548     onMonthChange: function () {
17549         this.store.load();
17550     },
17551     
17552     onMoreEventClick: function(e, el, more)
17553     {
17554         var _this = this;
17555         
17556         this.calpopover.placement = 'right';
17557         this.calpopover.setTitle('More');
17558         
17559         this.calpopover.setContent('');
17560         
17561         var ctr = this.calpopover.el.select('.popover-content', true).first();
17562         
17563         Roo.each(more, function(m){
17564             var cfg = {
17565                 cls : 'fc-event-hori fc-event-draggable',
17566                 html : m.title
17567             };
17568             var cg = ctr.createChild(cfg);
17569             
17570             cg.on('click', _this.onEventClick, _this, m);
17571         });
17572         
17573         this.calpopover.show(el);
17574         
17575         
17576     },
17577     
17578     onLoad: function () 
17579     {   
17580         this.calevents = [];
17581         var cal = this;
17582         
17583         if(this.store.getCount() > 0){
17584             this.store.data.each(function(d){
17585                cal.addItem({
17586                     id : d.data.id,
17587                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17588                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17589                     time : d.data.start_time,
17590                     title : d.data.title,
17591                     description : d.data.description,
17592                     venue : d.data.venue
17593                 });
17594             });
17595         }
17596         
17597         this.renderEvents();
17598         
17599         if(this.calevents.length && this.loadMask){
17600             this.maskEl.hide();
17601         }
17602     },
17603     
17604     onBeforeLoad: function()
17605     {
17606         this.clearEvents();
17607         if(this.loadMask){
17608             this.maskEl.show();
17609         }
17610     }
17611 });
17612
17613  
17614  /*
17615  * - LGPL
17616  *
17617  * element
17618  * 
17619  */
17620
17621 /**
17622  * @class Roo.bootstrap.Popover
17623  * @extends Roo.bootstrap.Component
17624  * Bootstrap Popover class
17625  * @cfg {String} html contents of the popover   (or false to use children..)
17626  * @cfg {String} title of popover (or false to hide)
17627  * @cfg {String} placement how it is placed
17628  * @cfg {String} trigger click || hover (or false to trigger manually)
17629  * @cfg {String} over what (parent or false to trigger manually.)
17630  * @cfg {Number} delay - delay before showing
17631  
17632  * @constructor
17633  * Create a new Popover
17634  * @param {Object} config The config object
17635  */
17636
17637 Roo.bootstrap.Popover = function(config){
17638     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17639     
17640     this.addEvents({
17641         // raw events
17642          /**
17643          * @event show
17644          * After the popover show
17645          * 
17646          * @param {Roo.bootstrap.Popover} this
17647          */
17648         "show" : true,
17649         /**
17650          * @event hide
17651          * After the popover hide
17652          * 
17653          * @param {Roo.bootstrap.Popover} this
17654          */
17655         "hide" : true
17656     });
17657 };
17658
17659 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17660     
17661     title: 'Fill in a title',
17662     html: false,
17663     
17664     placement : 'right',
17665     trigger : 'hover', // hover
17666     
17667     delay : 0,
17668     
17669     over: 'parent',
17670     
17671     can_build_overlaid : false,
17672     
17673     getChildContainer : function()
17674     {
17675         return this.el.select('.popover-content',true).first();
17676     },
17677     
17678     getAutoCreate : function(){
17679          
17680         var cfg = {
17681            cls : 'popover roo-dynamic',
17682            style: 'display:block',
17683            cn : [
17684                 {
17685                     cls : 'arrow'
17686                 },
17687                 {
17688                     cls : 'popover-inner',
17689                     cn : [
17690                         {
17691                             tag: 'h3',
17692                             cls: 'popover-title popover-header',
17693                             html : this.title
17694                         },
17695                         {
17696                             cls : 'popover-content popover-body',
17697                             html : this.html
17698                         }
17699                     ]
17700                     
17701                 }
17702            ]
17703         };
17704         
17705         return cfg;
17706     },
17707     setTitle: function(str)
17708     {
17709         this.title = str;
17710         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17711     },
17712     setContent: function(str)
17713     {
17714         this.html = str;
17715         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17716     },
17717     // as it get's added to the bottom of the page.
17718     onRender : function(ct, position)
17719     {
17720         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17721         if(!this.el){
17722             var cfg = Roo.apply({},  this.getAutoCreate());
17723             cfg.id = Roo.id();
17724             
17725             if (this.cls) {
17726                 cfg.cls += ' ' + this.cls;
17727             }
17728             if (this.style) {
17729                 cfg.style = this.style;
17730             }
17731             //Roo.log("adding to ");
17732             this.el = Roo.get(document.body).createChild(cfg, position);
17733 //            Roo.log(this.el);
17734         }
17735         this.initEvents();
17736     },
17737     
17738     initEvents : function()
17739     {
17740         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17741         this.el.enableDisplayMode('block');
17742         this.el.hide();
17743         if (this.over === false) {
17744             return; 
17745         }
17746         if (this.triggers === false) {
17747             return;
17748         }
17749         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17750         var triggers = this.trigger ? this.trigger.split(' ') : [];
17751         Roo.each(triggers, function(trigger) {
17752         
17753             if (trigger == 'click') {
17754                 on_el.on('click', this.toggle, this);
17755             } else if (trigger != 'manual') {
17756                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17757                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17758       
17759                 on_el.on(eventIn  ,this.enter, this);
17760                 on_el.on(eventOut, this.leave, this);
17761             }
17762         }, this);
17763         
17764     },
17765     
17766     
17767     // private
17768     timeout : null,
17769     hoverState : null,
17770     
17771     toggle : function () {
17772         this.hoverState == 'in' ? this.leave() : this.enter();
17773     },
17774     
17775     enter : function () {
17776         
17777         clearTimeout(this.timeout);
17778     
17779         this.hoverState = 'in';
17780     
17781         if (!this.delay || !this.delay.show) {
17782             this.show();
17783             return;
17784         }
17785         var _t = this;
17786         this.timeout = setTimeout(function () {
17787             if (_t.hoverState == 'in') {
17788                 _t.show();
17789             }
17790         }, this.delay.show)
17791     },
17792     
17793     leave : function() {
17794         clearTimeout(this.timeout);
17795     
17796         this.hoverState = 'out';
17797     
17798         if (!this.delay || !this.delay.hide) {
17799             this.hide();
17800             return;
17801         }
17802         var _t = this;
17803         this.timeout = setTimeout(function () {
17804             if (_t.hoverState == 'out') {
17805                 _t.hide();
17806             }
17807         }, this.delay.hide)
17808     },
17809     
17810     show : function (on_el)
17811     {
17812         if (!on_el) {
17813             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17814         }
17815         
17816         // set content.
17817         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17818         if (this.html !== false) {
17819             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17820         }
17821         this.el.removeClass([
17822             'fade','top','bottom', 'left', 'right','in',
17823             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17824         ]);
17825         if (!this.title.length) {
17826             this.el.select('.popover-title',true).hide();
17827         }
17828         
17829         var placement = typeof this.placement == 'function' ?
17830             this.placement.call(this, this.el, on_el) :
17831             this.placement;
17832             
17833         var autoToken = /\s?auto?\s?/i;
17834         var autoPlace = autoToken.test(placement);
17835         if (autoPlace) {
17836             placement = placement.replace(autoToken, '') || 'top';
17837         }
17838         
17839         //this.el.detach()
17840         //this.el.setXY([0,0]);
17841         this.el.show();
17842         this.el.dom.style.display='block';
17843         this.el.addClass(placement);
17844         
17845         //this.el.appendTo(on_el);
17846         
17847         var p = this.getPosition();
17848         var box = this.el.getBox();
17849         
17850         if (autoPlace) {
17851             // fixme..
17852         }
17853         var align = Roo.bootstrap.Popover.alignment[placement];
17854         
17855 //        Roo.log(align);
17856         this.el.alignTo(on_el, align[0],align[1]);
17857         //var arrow = this.el.select('.arrow',true).first();
17858         //arrow.set(align[2], 
17859         
17860         this.el.addClass('in');
17861         
17862         
17863         if (this.el.hasClass('fade')) {
17864             // fade it?
17865         }
17866         
17867         this.hoverState = 'in';
17868         
17869         this.fireEvent('show', this);
17870         
17871     },
17872     hide : function()
17873     {
17874         this.el.setXY([0,0]);
17875         this.el.removeClass('in');
17876         this.el.hide();
17877         this.hoverState = null;
17878         
17879         this.fireEvent('hide', this);
17880     }
17881     
17882 });
17883
17884 Roo.bootstrap.Popover.alignment = {
17885     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17886     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17887     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17888     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17889 };
17890
17891  /*
17892  * - LGPL
17893  *
17894  * Progress
17895  * 
17896  */
17897
17898 /**
17899  * @class Roo.bootstrap.Progress
17900  * @extends Roo.bootstrap.Component
17901  * Bootstrap Progress class
17902  * @cfg {Boolean} striped striped of the progress bar
17903  * @cfg {Boolean} active animated of the progress bar
17904  * 
17905  * 
17906  * @constructor
17907  * Create a new Progress
17908  * @param {Object} config The config object
17909  */
17910
17911 Roo.bootstrap.Progress = function(config){
17912     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17913 };
17914
17915 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17916     
17917     striped : false,
17918     active: false,
17919     
17920     getAutoCreate : function(){
17921         var cfg = {
17922             tag: 'div',
17923             cls: 'progress'
17924         };
17925         
17926         
17927         if(this.striped){
17928             cfg.cls += ' progress-striped';
17929         }
17930       
17931         if(this.active){
17932             cfg.cls += ' active';
17933         }
17934         
17935         
17936         return cfg;
17937     }
17938    
17939 });
17940
17941  
17942
17943  /*
17944  * - LGPL
17945  *
17946  * ProgressBar
17947  * 
17948  */
17949
17950 /**
17951  * @class Roo.bootstrap.ProgressBar
17952  * @extends Roo.bootstrap.Component
17953  * Bootstrap ProgressBar class
17954  * @cfg {Number} aria_valuenow aria-value now
17955  * @cfg {Number} aria_valuemin aria-value min
17956  * @cfg {Number} aria_valuemax aria-value max
17957  * @cfg {String} label label for the progress bar
17958  * @cfg {String} panel (success | info | warning | danger )
17959  * @cfg {String} role role of the progress bar
17960  * @cfg {String} sr_only text
17961  * 
17962  * 
17963  * @constructor
17964  * Create a new ProgressBar
17965  * @param {Object} config The config object
17966  */
17967
17968 Roo.bootstrap.ProgressBar = function(config){
17969     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17970 };
17971
17972 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17973     
17974     aria_valuenow : 0,
17975     aria_valuemin : 0,
17976     aria_valuemax : 100,
17977     label : false,
17978     panel : false,
17979     role : false,
17980     sr_only: false,
17981     
17982     getAutoCreate : function()
17983     {
17984         
17985         var cfg = {
17986             tag: 'div',
17987             cls: 'progress-bar',
17988             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17989         };
17990         
17991         if(this.sr_only){
17992             cfg.cn = {
17993                 tag: 'span',
17994                 cls: 'sr-only',
17995                 html: this.sr_only
17996             }
17997         }
17998         
17999         if(this.role){
18000             cfg.role = this.role;
18001         }
18002         
18003         if(this.aria_valuenow){
18004             cfg['aria-valuenow'] = this.aria_valuenow;
18005         }
18006         
18007         if(this.aria_valuemin){
18008             cfg['aria-valuemin'] = this.aria_valuemin;
18009         }
18010         
18011         if(this.aria_valuemax){
18012             cfg['aria-valuemax'] = this.aria_valuemax;
18013         }
18014         
18015         if(this.label && !this.sr_only){
18016             cfg.html = this.label;
18017         }
18018         
18019         if(this.panel){
18020             cfg.cls += ' progress-bar-' + this.panel;
18021         }
18022         
18023         return cfg;
18024     },
18025     
18026     update : function(aria_valuenow)
18027     {
18028         this.aria_valuenow = aria_valuenow;
18029         
18030         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18031     }
18032    
18033 });
18034
18035  
18036
18037  /*
18038  * - LGPL
18039  *
18040  * column
18041  * 
18042  */
18043
18044 /**
18045  * @class Roo.bootstrap.TabGroup
18046  * @extends Roo.bootstrap.Column
18047  * Bootstrap Column class
18048  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18049  * @cfg {Boolean} carousel true to make the group behave like a carousel
18050  * @cfg {Boolean} bullets show bullets for the panels
18051  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18052  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18053  * @cfg {Boolean} showarrow (true|false) show arrow default true
18054  * 
18055  * @constructor
18056  * Create a new TabGroup
18057  * @param {Object} config The config object
18058  */
18059
18060 Roo.bootstrap.TabGroup = function(config){
18061     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18062     if (!this.navId) {
18063         this.navId = Roo.id();
18064     }
18065     this.tabs = [];
18066     Roo.bootstrap.TabGroup.register(this);
18067     
18068 };
18069
18070 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18071     
18072     carousel : false,
18073     transition : false,
18074     bullets : 0,
18075     timer : 0,
18076     autoslide : false,
18077     slideFn : false,
18078     slideOnTouch : false,
18079     showarrow : true,
18080     
18081     getAutoCreate : function()
18082     {
18083         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18084         
18085         cfg.cls += ' tab-content';
18086         
18087         if (this.carousel) {
18088             cfg.cls += ' carousel slide';
18089             
18090             cfg.cn = [{
18091                cls : 'carousel-inner',
18092                cn : []
18093             }];
18094         
18095             if(this.bullets  && !Roo.isTouch){
18096                 
18097                 var bullets = {
18098                     cls : 'carousel-bullets',
18099                     cn : []
18100                 };
18101                
18102                 if(this.bullets_cls){
18103                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18104                 }
18105                 
18106                 bullets.cn.push({
18107                     cls : 'clear'
18108                 });
18109                 
18110                 cfg.cn[0].cn.push(bullets);
18111             }
18112             
18113             if(this.showarrow){
18114                 cfg.cn[0].cn.push({
18115                     tag : 'div',
18116                     class : 'carousel-arrow',
18117                     cn : [
18118                         {
18119                             tag : 'div',
18120                             class : 'carousel-prev',
18121                             cn : [
18122                                 {
18123                                     tag : 'i',
18124                                     class : 'fa fa-chevron-left'
18125                                 }
18126                             ]
18127                         },
18128                         {
18129                             tag : 'div',
18130                             class : 'carousel-next',
18131                             cn : [
18132                                 {
18133                                     tag : 'i',
18134                                     class : 'fa fa-chevron-right'
18135                                 }
18136                             ]
18137                         }
18138                     ]
18139                 });
18140             }
18141             
18142         }
18143         
18144         return cfg;
18145     },
18146     
18147     initEvents:  function()
18148     {
18149 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18150 //            this.el.on("touchstart", this.onTouchStart, this);
18151 //        }
18152         
18153         if(this.autoslide){
18154             var _this = this;
18155             
18156             this.slideFn = window.setInterval(function() {
18157                 _this.showPanelNext();
18158             }, this.timer);
18159         }
18160         
18161         if(this.showarrow){
18162             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18163             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18164         }
18165         
18166         
18167     },
18168     
18169 //    onTouchStart : function(e, el, o)
18170 //    {
18171 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18172 //            return;
18173 //        }
18174 //        
18175 //        this.showPanelNext();
18176 //    },
18177     
18178     
18179     getChildContainer : function()
18180     {
18181         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18182     },
18183     
18184     /**
18185     * register a Navigation item
18186     * @param {Roo.bootstrap.NavItem} the navitem to add
18187     */
18188     register : function(item)
18189     {
18190         this.tabs.push( item);
18191         item.navId = this.navId; // not really needed..
18192         this.addBullet();
18193     
18194     },
18195     
18196     getActivePanel : function()
18197     {
18198         var r = false;
18199         Roo.each(this.tabs, function(t) {
18200             if (t.active) {
18201                 r = t;
18202                 return false;
18203             }
18204             return null;
18205         });
18206         return r;
18207         
18208     },
18209     getPanelByName : function(n)
18210     {
18211         var r = false;
18212         Roo.each(this.tabs, function(t) {
18213             if (t.tabId == n) {
18214                 r = t;
18215                 return false;
18216             }
18217             return null;
18218         });
18219         return r;
18220     },
18221     indexOfPanel : function(p)
18222     {
18223         var r = false;
18224         Roo.each(this.tabs, function(t,i) {
18225             if (t.tabId == p.tabId) {
18226                 r = i;
18227                 return false;
18228             }
18229             return null;
18230         });
18231         return r;
18232     },
18233     /**
18234      * show a specific panel
18235      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18236      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18237      */
18238     showPanel : function (pan)
18239     {
18240         if(this.transition || typeof(pan) == 'undefined'){
18241             Roo.log("waiting for the transitionend");
18242             return;
18243         }
18244         
18245         if (typeof(pan) == 'number') {
18246             pan = this.tabs[pan];
18247         }
18248         
18249         if (typeof(pan) == 'string') {
18250             pan = this.getPanelByName(pan);
18251         }
18252         
18253         var cur = this.getActivePanel();
18254         
18255         if(!pan || !cur){
18256             Roo.log('pan or acitve pan is undefined');
18257             return false;
18258         }
18259         
18260         if (pan.tabId == this.getActivePanel().tabId) {
18261             return true;
18262         }
18263         
18264         if (false === cur.fireEvent('beforedeactivate')) {
18265             return false;
18266         }
18267         
18268         if(this.bullets > 0 && !Roo.isTouch){
18269             this.setActiveBullet(this.indexOfPanel(pan));
18270         }
18271         
18272         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18273             
18274             this.transition = true;
18275             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18276             var lr = dir == 'next' ? 'left' : 'right';
18277             pan.el.addClass(dir); // or prev
18278             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18279             cur.el.addClass(lr); // or right
18280             pan.el.addClass(lr);
18281             
18282             var _this = this;
18283             cur.el.on('transitionend', function() {
18284                 Roo.log("trans end?");
18285                 
18286                 pan.el.removeClass([lr,dir]);
18287                 pan.setActive(true);
18288                 
18289                 cur.el.removeClass([lr]);
18290                 cur.setActive(false);
18291                 
18292                 _this.transition = false;
18293                 
18294             }, this, { single:  true } );
18295             
18296             return true;
18297         }
18298         
18299         cur.setActive(false);
18300         pan.setActive(true);
18301         
18302         return true;
18303         
18304     },
18305     showPanelNext : function()
18306     {
18307         var i = this.indexOfPanel(this.getActivePanel());
18308         
18309         if (i >= this.tabs.length - 1 && !this.autoslide) {
18310             return;
18311         }
18312         
18313         if (i >= this.tabs.length - 1 && this.autoslide) {
18314             i = -1;
18315         }
18316         
18317         this.showPanel(this.tabs[i+1]);
18318     },
18319     
18320     showPanelPrev : function()
18321     {
18322         var i = this.indexOfPanel(this.getActivePanel());
18323         
18324         if (i  < 1 && !this.autoslide) {
18325             return;
18326         }
18327         
18328         if (i < 1 && this.autoslide) {
18329             i = this.tabs.length;
18330         }
18331         
18332         this.showPanel(this.tabs[i-1]);
18333     },
18334     
18335     
18336     addBullet: function()
18337     {
18338         if(!this.bullets || Roo.isTouch){
18339             return;
18340         }
18341         var ctr = this.el.select('.carousel-bullets',true).first();
18342         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18343         var bullet = ctr.createChild({
18344             cls : 'bullet bullet-' + i
18345         },ctr.dom.lastChild);
18346         
18347         
18348         var _this = this;
18349         
18350         bullet.on('click', (function(e, el, o, ii, t){
18351
18352             e.preventDefault();
18353
18354             this.showPanel(ii);
18355
18356             if(this.autoslide && this.slideFn){
18357                 clearInterval(this.slideFn);
18358                 this.slideFn = window.setInterval(function() {
18359                     _this.showPanelNext();
18360                 }, this.timer);
18361             }
18362
18363         }).createDelegate(this, [i, bullet], true));
18364                 
18365         
18366     },
18367      
18368     setActiveBullet : function(i)
18369     {
18370         if(Roo.isTouch){
18371             return;
18372         }
18373         
18374         Roo.each(this.el.select('.bullet', true).elements, function(el){
18375             el.removeClass('selected');
18376         });
18377
18378         var bullet = this.el.select('.bullet-' + i, true).first();
18379         
18380         if(!bullet){
18381             return;
18382         }
18383         
18384         bullet.addClass('selected');
18385     }
18386     
18387     
18388   
18389 });
18390
18391  
18392
18393  
18394  
18395 Roo.apply(Roo.bootstrap.TabGroup, {
18396     
18397     groups: {},
18398      /**
18399     * register a Navigation Group
18400     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18401     */
18402     register : function(navgrp)
18403     {
18404         this.groups[navgrp.navId] = navgrp;
18405         
18406     },
18407     /**
18408     * fetch a Navigation Group based on the navigation ID
18409     * if one does not exist , it will get created.
18410     * @param {string} the navgroup to add
18411     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18412     */
18413     get: function(navId) {
18414         if (typeof(this.groups[navId]) == 'undefined') {
18415             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18416         }
18417         return this.groups[navId] ;
18418     }
18419     
18420     
18421     
18422 });
18423
18424  /*
18425  * - LGPL
18426  *
18427  * TabPanel
18428  * 
18429  */
18430
18431 /**
18432  * @class Roo.bootstrap.TabPanel
18433  * @extends Roo.bootstrap.Component
18434  * Bootstrap TabPanel class
18435  * @cfg {Boolean} active panel active
18436  * @cfg {String} html panel content
18437  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18438  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18439  * @cfg {String} href click to link..
18440  * 
18441  * 
18442  * @constructor
18443  * Create a new TabPanel
18444  * @param {Object} config The config object
18445  */
18446
18447 Roo.bootstrap.TabPanel = function(config){
18448     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18449     this.addEvents({
18450         /**
18451              * @event changed
18452              * Fires when the active status changes
18453              * @param {Roo.bootstrap.TabPanel} this
18454              * @param {Boolean} state the new state
18455             
18456          */
18457         'changed': true,
18458         /**
18459              * @event beforedeactivate
18460              * Fires before a tab is de-activated - can be used to do validation on a form.
18461              * @param {Roo.bootstrap.TabPanel} this
18462              * @return {Boolean} false if there is an error
18463             
18464          */
18465         'beforedeactivate': true
18466      });
18467     
18468     this.tabId = this.tabId || Roo.id();
18469   
18470 };
18471
18472 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18473     
18474     active: false,
18475     html: false,
18476     tabId: false,
18477     navId : false,
18478     href : '',
18479     
18480     getAutoCreate : function(){
18481         var cfg = {
18482             tag: 'div',
18483             // item is needed for carousel - not sure if it has any effect otherwise
18484             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18485             html: this.html || ''
18486         };
18487         
18488         if(this.active){
18489             cfg.cls += ' active';
18490         }
18491         
18492         if(this.tabId){
18493             cfg.tabId = this.tabId;
18494         }
18495         
18496         
18497         return cfg;
18498     },
18499     
18500     initEvents:  function()
18501     {
18502         var p = this.parent();
18503         
18504         this.navId = this.navId || p.navId;
18505         
18506         if (typeof(this.navId) != 'undefined') {
18507             // not really needed.. but just in case.. parent should be a NavGroup.
18508             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18509             
18510             tg.register(this);
18511             
18512             var i = tg.tabs.length - 1;
18513             
18514             if(this.active && tg.bullets > 0 && i < tg.bullets){
18515                 tg.setActiveBullet(i);
18516             }
18517         }
18518         
18519         this.el.on('click', this.onClick, this);
18520         
18521         if(Roo.isTouch){
18522             this.el.on("touchstart", this.onTouchStart, this);
18523             this.el.on("touchmove", this.onTouchMove, this);
18524             this.el.on("touchend", this.onTouchEnd, this);
18525         }
18526         
18527     },
18528     
18529     onRender : function(ct, position)
18530     {
18531         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18532     },
18533     
18534     setActive : function(state)
18535     {
18536         Roo.log("panel - set active " + this.tabId + "=" + state);
18537         
18538         this.active = state;
18539         if (!state) {
18540             this.el.removeClass('active');
18541             
18542         } else  if (!this.el.hasClass('active')) {
18543             this.el.addClass('active');
18544         }
18545         
18546         this.fireEvent('changed', this, state);
18547     },
18548     
18549     onClick : function(e)
18550     {
18551         e.preventDefault();
18552         
18553         if(!this.href.length){
18554             return;
18555         }
18556         
18557         window.location.href = this.href;
18558     },
18559     
18560     startX : 0,
18561     startY : 0,
18562     endX : 0,
18563     endY : 0,
18564     swiping : false,
18565     
18566     onTouchStart : function(e)
18567     {
18568         this.swiping = false;
18569         
18570         this.startX = e.browserEvent.touches[0].clientX;
18571         this.startY = e.browserEvent.touches[0].clientY;
18572     },
18573     
18574     onTouchMove : function(e)
18575     {
18576         this.swiping = true;
18577         
18578         this.endX = e.browserEvent.touches[0].clientX;
18579         this.endY = e.browserEvent.touches[0].clientY;
18580     },
18581     
18582     onTouchEnd : function(e)
18583     {
18584         if(!this.swiping){
18585             this.onClick(e);
18586             return;
18587         }
18588         
18589         var tabGroup = this.parent();
18590         
18591         if(this.endX > this.startX){ // swiping right
18592             tabGroup.showPanelPrev();
18593             return;
18594         }
18595         
18596         if(this.startX > this.endX){ // swiping left
18597             tabGroup.showPanelNext();
18598             return;
18599         }
18600     }
18601     
18602     
18603 });
18604  
18605
18606  
18607
18608  /*
18609  * - LGPL
18610  *
18611  * DateField
18612  * 
18613  */
18614
18615 /**
18616  * @class Roo.bootstrap.DateField
18617  * @extends Roo.bootstrap.Input
18618  * Bootstrap DateField class
18619  * @cfg {Number} weekStart default 0
18620  * @cfg {String} viewMode default empty, (months|years)
18621  * @cfg {String} minViewMode default empty, (months|years)
18622  * @cfg {Number} startDate default -Infinity
18623  * @cfg {Number} endDate default Infinity
18624  * @cfg {Boolean} todayHighlight default false
18625  * @cfg {Boolean} todayBtn default false
18626  * @cfg {Boolean} calendarWeeks default false
18627  * @cfg {Object} daysOfWeekDisabled default empty
18628  * @cfg {Boolean} singleMode default false (true | false)
18629  * 
18630  * @cfg {Boolean} keyboardNavigation default true
18631  * @cfg {String} language default en
18632  * 
18633  * @constructor
18634  * Create a new DateField
18635  * @param {Object} config The config object
18636  */
18637
18638 Roo.bootstrap.DateField = function(config){
18639     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18640      this.addEvents({
18641             /**
18642              * @event show
18643              * Fires when this field show.
18644              * @param {Roo.bootstrap.DateField} this
18645              * @param {Mixed} date The date value
18646              */
18647             show : true,
18648             /**
18649              * @event show
18650              * Fires when this field hide.
18651              * @param {Roo.bootstrap.DateField} this
18652              * @param {Mixed} date The date value
18653              */
18654             hide : true,
18655             /**
18656              * @event select
18657              * Fires when select a date.
18658              * @param {Roo.bootstrap.DateField} this
18659              * @param {Mixed} date The date value
18660              */
18661             select : true,
18662             /**
18663              * @event beforeselect
18664              * Fires when before select a date.
18665              * @param {Roo.bootstrap.DateField} this
18666              * @param {Mixed} date The date value
18667              */
18668             beforeselect : true
18669         });
18670 };
18671
18672 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18673     
18674     /**
18675      * @cfg {String} format
18676      * The default date format string which can be overriden for localization support.  The format must be
18677      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18678      */
18679     format : "m/d/y",
18680     /**
18681      * @cfg {String} altFormats
18682      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18683      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18684      */
18685     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18686     
18687     weekStart : 0,
18688     
18689     viewMode : '',
18690     
18691     minViewMode : '',
18692     
18693     todayHighlight : false,
18694     
18695     todayBtn: false,
18696     
18697     language: 'en',
18698     
18699     keyboardNavigation: true,
18700     
18701     calendarWeeks: false,
18702     
18703     startDate: -Infinity,
18704     
18705     endDate: Infinity,
18706     
18707     daysOfWeekDisabled: [],
18708     
18709     _events: [],
18710     
18711     singleMode : false,
18712     
18713     UTCDate: function()
18714     {
18715         return new Date(Date.UTC.apply(Date, arguments));
18716     },
18717     
18718     UTCToday: function()
18719     {
18720         var today = new Date();
18721         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18722     },
18723     
18724     getDate: function() {
18725             var d = this.getUTCDate();
18726             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18727     },
18728     
18729     getUTCDate: function() {
18730             return this.date;
18731     },
18732     
18733     setDate: function(d) {
18734             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18735     },
18736     
18737     setUTCDate: function(d) {
18738             this.date = d;
18739             this.setValue(this.formatDate(this.date));
18740     },
18741         
18742     onRender: function(ct, position)
18743     {
18744         
18745         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18746         
18747         this.language = this.language || 'en';
18748         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18749         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18750         
18751         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18752         this.format = this.format || 'm/d/y';
18753         this.isInline = false;
18754         this.isInput = true;
18755         this.component = this.el.select('.add-on', true).first() || false;
18756         this.component = (this.component && this.component.length === 0) ? false : this.component;
18757         this.hasInput = this.component && this.inputEl().length;
18758         
18759         if (typeof(this.minViewMode === 'string')) {
18760             switch (this.minViewMode) {
18761                 case 'months':
18762                     this.minViewMode = 1;
18763                     break;
18764                 case 'years':
18765                     this.minViewMode = 2;
18766                     break;
18767                 default:
18768                     this.minViewMode = 0;
18769                     break;
18770             }
18771         }
18772         
18773         if (typeof(this.viewMode === 'string')) {
18774             switch (this.viewMode) {
18775                 case 'months':
18776                     this.viewMode = 1;
18777                     break;
18778                 case 'years':
18779                     this.viewMode = 2;
18780                     break;
18781                 default:
18782                     this.viewMode = 0;
18783                     break;
18784             }
18785         }
18786                 
18787         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18788         
18789 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18790         
18791         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18792         
18793         this.picker().on('mousedown', this.onMousedown, this);
18794         this.picker().on('click', this.onClick, this);
18795         
18796         this.picker().addClass('datepicker-dropdown');
18797         
18798         this.startViewMode = this.viewMode;
18799         
18800         if(this.singleMode){
18801             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18802                 v.setVisibilityMode(Roo.Element.DISPLAY);
18803                 v.hide();
18804             });
18805             
18806             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18807                 v.setStyle('width', '189px');
18808             });
18809         }
18810         
18811         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18812             if(!this.calendarWeeks){
18813                 v.remove();
18814                 return;
18815             }
18816             
18817             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18818             v.attr('colspan', function(i, val){
18819                 return parseInt(val) + 1;
18820             });
18821         });
18822                         
18823         
18824         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18825         
18826         this.setStartDate(this.startDate);
18827         this.setEndDate(this.endDate);
18828         
18829         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18830         
18831         this.fillDow();
18832         this.fillMonths();
18833         this.update();
18834         this.showMode();
18835         
18836         if(this.isInline) {
18837             this.showPopup();
18838         }
18839     },
18840     
18841     picker : function()
18842     {
18843         return this.pickerEl;
18844 //        return this.el.select('.datepicker', true).first();
18845     },
18846     
18847     fillDow: function()
18848     {
18849         var dowCnt = this.weekStart;
18850         
18851         var dow = {
18852             tag: 'tr',
18853             cn: [
18854                 
18855             ]
18856         };
18857         
18858         if(this.calendarWeeks){
18859             dow.cn.push({
18860                 tag: 'th',
18861                 cls: 'cw',
18862                 html: '&nbsp;'
18863             })
18864         }
18865         
18866         while (dowCnt < this.weekStart + 7) {
18867             dow.cn.push({
18868                 tag: 'th',
18869                 cls: 'dow',
18870                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18871             });
18872         }
18873         
18874         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18875     },
18876     
18877     fillMonths: function()
18878     {    
18879         var i = 0;
18880         var months = this.picker().select('>.datepicker-months td', true).first();
18881         
18882         months.dom.innerHTML = '';
18883         
18884         while (i < 12) {
18885             var month = {
18886                 tag: 'span',
18887                 cls: 'month',
18888                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18889             };
18890             
18891             months.createChild(month);
18892         }
18893         
18894     },
18895     
18896     update: function()
18897     {
18898         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;
18899         
18900         if (this.date < this.startDate) {
18901             this.viewDate = new Date(this.startDate);
18902         } else if (this.date > this.endDate) {
18903             this.viewDate = new Date(this.endDate);
18904         } else {
18905             this.viewDate = new Date(this.date);
18906         }
18907         
18908         this.fill();
18909     },
18910     
18911     fill: function() 
18912     {
18913         var d = new Date(this.viewDate),
18914                 year = d.getUTCFullYear(),
18915                 month = d.getUTCMonth(),
18916                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18917                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18918                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18919                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18920                 currentDate = this.date && this.date.valueOf(),
18921                 today = this.UTCToday();
18922         
18923         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18924         
18925 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18926         
18927 //        this.picker.select('>tfoot th.today').
18928 //                                              .text(dates[this.language].today)
18929 //                                              .toggle(this.todayBtn !== false);
18930     
18931         this.updateNavArrows();
18932         this.fillMonths();
18933                                                 
18934         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18935         
18936         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18937          
18938         prevMonth.setUTCDate(day);
18939         
18940         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18941         
18942         var nextMonth = new Date(prevMonth);
18943         
18944         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18945         
18946         nextMonth = nextMonth.valueOf();
18947         
18948         var fillMonths = false;
18949         
18950         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18951         
18952         while(prevMonth.valueOf() <= nextMonth) {
18953             var clsName = '';
18954             
18955             if (prevMonth.getUTCDay() === this.weekStart) {
18956                 if(fillMonths){
18957                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18958                 }
18959                     
18960                 fillMonths = {
18961                     tag: 'tr',
18962                     cn: []
18963                 };
18964                 
18965                 if(this.calendarWeeks){
18966                     // ISO 8601: First week contains first thursday.
18967                     // ISO also states week starts on Monday, but we can be more abstract here.
18968                     var
18969                     // Start of current week: based on weekstart/current date
18970                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18971                     // Thursday of this week
18972                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18973                     // First Thursday of year, year from thursday
18974                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18975                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18976                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18977                     
18978                     fillMonths.cn.push({
18979                         tag: 'td',
18980                         cls: 'cw',
18981                         html: calWeek
18982                     });
18983                 }
18984             }
18985             
18986             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18987                 clsName += ' old';
18988             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18989                 clsName += ' new';
18990             }
18991             if (this.todayHighlight &&
18992                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18993                 prevMonth.getUTCMonth() == today.getMonth() &&
18994                 prevMonth.getUTCDate() == today.getDate()) {
18995                 clsName += ' today';
18996             }
18997             
18998             if (currentDate && prevMonth.valueOf() === currentDate) {
18999                 clsName += ' active';
19000             }
19001             
19002             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19003                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19004                     clsName += ' disabled';
19005             }
19006             
19007             fillMonths.cn.push({
19008                 tag: 'td',
19009                 cls: 'day ' + clsName,
19010                 html: prevMonth.getDate()
19011             });
19012             
19013             prevMonth.setDate(prevMonth.getDate()+1);
19014         }
19015           
19016         var currentYear = this.date && this.date.getUTCFullYear();
19017         var currentMonth = this.date && this.date.getUTCMonth();
19018         
19019         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19020         
19021         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19022             v.removeClass('active');
19023             
19024             if(currentYear === year && k === currentMonth){
19025                 v.addClass('active');
19026             }
19027             
19028             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19029                 v.addClass('disabled');
19030             }
19031             
19032         });
19033         
19034         
19035         year = parseInt(year/10, 10) * 10;
19036         
19037         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19038         
19039         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19040         
19041         year -= 1;
19042         for (var i = -1; i < 11; i++) {
19043             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19044                 tag: 'span',
19045                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19046                 html: year
19047             });
19048             
19049             year += 1;
19050         }
19051     },
19052     
19053     showMode: function(dir) 
19054     {
19055         if (dir) {
19056             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19057         }
19058         
19059         Roo.each(this.picker().select('>div',true).elements, function(v){
19060             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19061             v.hide();
19062         });
19063         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19064     },
19065     
19066     place: function()
19067     {
19068         if(this.isInline) {
19069             return;
19070         }
19071         
19072         this.picker().removeClass(['bottom', 'top']);
19073         
19074         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19075             /*
19076              * place to the top of element!
19077              *
19078              */
19079             
19080             this.picker().addClass('top');
19081             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19082             
19083             return;
19084         }
19085         
19086         this.picker().addClass('bottom');
19087         
19088         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19089     },
19090     
19091     parseDate : function(value)
19092     {
19093         if(!value || value instanceof Date){
19094             return value;
19095         }
19096         var v = Date.parseDate(value, this.format);
19097         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19098             v = Date.parseDate(value, 'Y-m-d');
19099         }
19100         if(!v && this.altFormats){
19101             if(!this.altFormatsArray){
19102                 this.altFormatsArray = this.altFormats.split("|");
19103             }
19104             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19105                 v = Date.parseDate(value, this.altFormatsArray[i]);
19106             }
19107         }
19108         return v;
19109     },
19110     
19111     formatDate : function(date, fmt)
19112     {   
19113         return (!date || !(date instanceof Date)) ?
19114         date : date.dateFormat(fmt || this.format);
19115     },
19116     
19117     onFocus : function()
19118     {
19119         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19120         this.showPopup();
19121     },
19122     
19123     onBlur : function()
19124     {
19125         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19126         
19127         var d = this.inputEl().getValue();
19128         
19129         this.setValue(d);
19130                 
19131         this.hidePopup();
19132     },
19133     
19134     showPopup : function()
19135     {
19136         this.picker().show();
19137         this.update();
19138         this.place();
19139         
19140         this.fireEvent('showpopup', this, this.date);
19141     },
19142     
19143     hidePopup : function()
19144     {
19145         if(this.isInline) {
19146             return;
19147         }
19148         this.picker().hide();
19149         this.viewMode = this.startViewMode;
19150         this.showMode();
19151         
19152         this.fireEvent('hidepopup', this, this.date);
19153         
19154     },
19155     
19156     onMousedown: function(e)
19157     {
19158         e.stopPropagation();
19159         e.preventDefault();
19160     },
19161     
19162     keyup: function(e)
19163     {
19164         Roo.bootstrap.DateField.superclass.keyup.call(this);
19165         this.update();
19166     },
19167
19168     setValue: function(v)
19169     {
19170         if(this.fireEvent('beforeselect', this, v) !== false){
19171             var d = new Date(this.parseDate(v) ).clearTime();
19172         
19173             if(isNaN(d.getTime())){
19174                 this.date = this.viewDate = '';
19175                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19176                 return;
19177             }
19178
19179             v = this.formatDate(d);
19180
19181             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19182
19183             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19184
19185             this.update();
19186
19187             this.fireEvent('select', this, this.date);
19188         }
19189     },
19190     
19191     getValue: function()
19192     {
19193         return this.formatDate(this.date);
19194     },
19195     
19196     fireKey: function(e)
19197     {
19198         if (!this.picker().isVisible()){
19199             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19200                 this.showPopup();
19201             }
19202             return;
19203         }
19204         
19205         var dateChanged = false,
19206         dir, day, month,
19207         newDate, newViewDate;
19208         
19209         switch(e.keyCode){
19210             case 27: // escape
19211                 this.hidePopup();
19212                 e.preventDefault();
19213                 break;
19214             case 37: // left
19215             case 39: // right
19216                 if (!this.keyboardNavigation) {
19217                     break;
19218                 }
19219                 dir = e.keyCode == 37 ? -1 : 1;
19220                 
19221                 if (e.ctrlKey){
19222                     newDate = this.moveYear(this.date, dir);
19223                     newViewDate = this.moveYear(this.viewDate, dir);
19224                 } else if (e.shiftKey){
19225                     newDate = this.moveMonth(this.date, dir);
19226                     newViewDate = this.moveMonth(this.viewDate, dir);
19227                 } else {
19228                     newDate = new Date(this.date);
19229                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19230                     newViewDate = new Date(this.viewDate);
19231                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19232                 }
19233                 if (this.dateWithinRange(newDate)){
19234                     this.date = newDate;
19235                     this.viewDate = newViewDate;
19236                     this.setValue(this.formatDate(this.date));
19237 //                    this.update();
19238                     e.preventDefault();
19239                     dateChanged = true;
19240                 }
19241                 break;
19242             case 38: // up
19243             case 40: // down
19244                 if (!this.keyboardNavigation) {
19245                     break;
19246                 }
19247                 dir = e.keyCode == 38 ? -1 : 1;
19248                 if (e.ctrlKey){
19249                     newDate = this.moveYear(this.date, dir);
19250                     newViewDate = this.moveYear(this.viewDate, dir);
19251                 } else if (e.shiftKey){
19252                     newDate = this.moveMonth(this.date, dir);
19253                     newViewDate = this.moveMonth(this.viewDate, dir);
19254                 } else {
19255                     newDate = new Date(this.date);
19256                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19257                     newViewDate = new Date(this.viewDate);
19258                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19259                 }
19260                 if (this.dateWithinRange(newDate)){
19261                     this.date = newDate;
19262                     this.viewDate = newViewDate;
19263                     this.setValue(this.formatDate(this.date));
19264 //                    this.update();
19265                     e.preventDefault();
19266                     dateChanged = true;
19267                 }
19268                 break;
19269             case 13: // enter
19270                 this.setValue(this.formatDate(this.date));
19271                 this.hidePopup();
19272                 e.preventDefault();
19273                 break;
19274             case 9: // tab
19275                 this.setValue(this.formatDate(this.date));
19276                 this.hidePopup();
19277                 break;
19278             case 16: // shift
19279             case 17: // ctrl
19280             case 18: // alt
19281                 break;
19282             default :
19283                 this.hidePopup();
19284                 
19285         }
19286     },
19287     
19288     
19289     onClick: function(e) 
19290     {
19291         e.stopPropagation();
19292         e.preventDefault();
19293         
19294         var target = e.getTarget();
19295         
19296         if(target.nodeName.toLowerCase() === 'i'){
19297             target = Roo.get(target).dom.parentNode;
19298         }
19299         
19300         var nodeName = target.nodeName;
19301         var className = target.className;
19302         var html = target.innerHTML;
19303         //Roo.log(nodeName);
19304         
19305         switch(nodeName.toLowerCase()) {
19306             case 'th':
19307                 switch(className) {
19308                     case 'switch':
19309                         this.showMode(1);
19310                         break;
19311                     case 'prev':
19312                     case 'next':
19313                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19314                         switch(this.viewMode){
19315                                 case 0:
19316                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19317                                         break;
19318                                 case 1:
19319                                 case 2:
19320                                         this.viewDate = this.moveYear(this.viewDate, dir);
19321                                         break;
19322                         }
19323                         this.fill();
19324                         break;
19325                     case 'today':
19326                         var date = new Date();
19327                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19328 //                        this.fill()
19329                         this.setValue(this.formatDate(this.date));
19330                         
19331                         this.hidePopup();
19332                         break;
19333                 }
19334                 break;
19335             case 'span':
19336                 if (className.indexOf('disabled') < 0) {
19337                     this.viewDate.setUTCDate(1);
19338                     if (className.indexOf('month') > -1) {
19339                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19340                     } else {
19341                         var year = parseInt(html, 10) || 0;
19342                         this.viewDate.setUTCFullYear(year);
19343                         
19344                     }
19345                     
19346                     if(this.singleMode){
19347                         this.setValue(this.formatDate(this.viewDate));
19348                         this.hidePopup();
19349                         return;
19350                     }
19351                     
19352                     this.showMode(-1);
19353                     this.fill();
19354                 }
19355                 break;
19356                 
19357             case 'td':
19358                 //Roo.log(className);
19359                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19360                     var day = parseInt(html, 10) || 1;
19361                     var year = this.viewDate.getUTCFullYear(),
19362                         month = this.viewDate.getUTCMonth();
19363
19364                     if (className.indexOf('old') > -1) {
19365                         if(month === 0 ){
19366                             month = 11;
19367                             year -= 1;
19368                         }else{
19369                             month -= 1;
19370                         }
19371                     } else if (className.indexOf('new') > -1) {
19372                         if (month == 11) {
19373                             month = 0;
19374                             year += 1;
19375                         } else {
19376                             month += 1;
19377                         }
19378                     }
19379                     //Roo.log([year,month,day]);
19380                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19381                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19382 //                    this.fill();
19383                     //Roo.log(this.formatDate(this.date));
19384                     this.setValue(this.formatDate(this.date));
19385                     this.hidePopup();
19386                 }
19387                 break;
19388         }
19389     },
19390     
19391     setStartDate: function(startDate)
19392     {
19393         this.startDate = startDate || -Infinity;
19394         if (this.startDate !== -Infinity) {
19395             this.startDate = this.parseDate(this.startDate);
19396         }
19397         this.update();
19398         this.updateNavArrows();
19399     },
19400
19401     setEndDate: function(endDate)
19402     {
19403         this.endDate = endDate || Infinity;
19404         if (this.endDate !== Infinity) {
19405             this.endDate = this.parseDate(this.endDate);
19406         }
19407         this.update();
19408         this.updateNavArrows();
19409     },
19410     
19411     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19412     {
19413         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19414         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19415             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19416         }
19417         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19418             return parseInt(d, 10);
19419         });
19420         this.update();
19421         this.updateNavArrows();
19422     },
19423     
19424     updateNavArrows: function() 
19425     {
19426         if(this.singleMode){
19427             return;
19428         }
19429         
19430         var d = new Date(this.viewDate),
19431         year = d.getUTCFullYear(),
19432         month = d.getUTCMonth();
19433         
19434         Roo.each(this.picker().select('.prev', true).elements, function(v){
19435             v.show();
19436             switch (this.viewMode) {
19437                 case 0:
19438
19439                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19440                         v.hide();
19441                     }
19442                     break;
19443                 case 1:
19444                 case 2:
19445                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19446                         v.hide();
19447                     }
19448                     break;
19449             }
19450         });
19451         
19452         Roo.each(this.picker().select('.next', true).elements, function(v){
19453             v.show();
19454             switch (this.viewMode) {
19455                 case 0:
19456
19457                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19458                         v.hide();
19459                     }
19460                     break;
19461                 case 1:
19462                 case 2:
19463                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19464                         v.hide();
19465                     }
19466                     break;
19467             }
19468         })
19469     },
19470     
19471     moveMonth: function(date, dir)
19472     {
19473         if (!dir) {
19474             return date;
19475         }
19476         var new_date = new Date(date.valueOf()),
19477         day = new_date.getUTCDate(),
19478         month = new_date.getUTCMonth(),
19479         mag = Math.abs(dir),
19480         new_month, test;
19481         dir = dir > 0 ? 1 : -1;
19482         if (mag == 1){
19483             test = dir == -1
19484             // If going back one month, make sure month is not current month
19485             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19486             ? function(){
19487                 return new_date.getUTCMonth() == month;
19488             }
19489             // If going forward one month, make sure month is as expected
19490             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19491             : function(){
19492                 return new_date.getUTCMonth() != new_month;
19493             };
19494             new_month = month + dir;
19495             new_date.setUTCMonth(new_month);
19496             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19497             if (new_month < 0 || new_month > 11) {
19498                 new_month = (new_month + 12) % 12;
19499             }
19500         } else {
19501             // For magnitudes >1, move one month at a time...
19502             for (var i=0; i<mag; i++) {
19503                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19504                 new_date = this.moveMonth(new_date, dir);
19505             }
19506             // ...then reset the day, keeping it in the new month
19507             new_month = new_date.getUTCMonth();
19508             new_date.setUTCDate(day);
19509             test = function(){
19510                 return new_month != new_date.getUTCMonth();
19511             };
19512         }
19513         // Common date-resetting loop -- if date is beyond end of month, make it
19514         // end of month
19515         while (test()){
19516             new_date.setUTCDate(--day);
19517             new_date.setUTCMonth(new_month);
19518         }
19519         return new_date;
19520     },
19521
19522     moveYear: function(date, dir)
19523     {
19524         return this.moveMonth(date, dir*12);
19525     },
19526
19527     dateWithinRange: function(date)
19528     {
19529         return date >= this.startDate && date <= this.endDate;
19530     },
19531
19532     
19533     remove: function() 
19534     {
19535         this.picker().remove();
19536     },
19537     
19538     validateValue : function(value)
19539     {
19540         if(this.getVisibilityEl().hasClass('hidden')){
19541             return true;
19542         }
19543         
19544         if(value.length < 1)  {
19545             if(this.allowBlank){
19546                 return true;
19547             }
19548             return false;
19549         }
19550         
19551         if(value.length < this.minLength){
19552             return false;
19553         }
19554         if(value.length > this.maxLength){
19555             return false;
19556         }
19557         if(this.vtype){
19558             var vt = Roo.form.VTypes;
19559             if(!vt[this.vtype](value, this)){
19560                 return false;
19561             }
19562         }
19563         if(typeof this.validator == "function"){
19564             var msg = this.validator(value);
19565             if(msg !== true){
19566                 return false;
19567             }
19568         }
19569         
19570         if(this.regex && !this.regex.test(value)){
19571             return false;
19572         }
19573         
19574         if(typeof(this.parseDate(value)) == 'undefined'){
19575             return false;
19576         }
19577         
19578         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19579             return false;
19580         }      
19581         
19582         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19583             return false;
19584         } 
19585         
19586         
19587         return true;
19588     },
19589     
19590     reset : function()
19591     {
19592         this.date = this.viewDate = '';
19593         
19594         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19595     }
19596    
19597 });
19598
19599 Roo.apply(Roo.bootstrap.DateField,  {
19600     
19601     head : {
19602         tag: 'thead',
19603         cn: [
19604         {
19605             tag: 'tr',
19606             cn: [
19607             {
19608                 tag: 'th',
19609                 cls: 'prev',
19610                 html: '<i class="fa fa-arrow-left"/>'
19611             },
19612             {
19613                 tag: 'th',
19614                 cls: 'switch',
19615                 colspan: '5'
19616             },
19617             {
19618                 tag: 'th',
19619                 cls: 'next',
19620                 html: '<i class="fa fa-arrow-right"/>'
19621             }
19622
19623             ]
19624         }
19625         ]
19626     },
19627     
19628     content : {
19629         tag: 'tbody',
19630         cn: [
19631         {
19632             tag: 'tr',
19633             cn: [
19634             {
19635                 tag: 'td',
19636                 colspan: '7'
19637             }
19638             ]
19639         }
19640         ]
19641     },
19642     
19643     footer : {
19644         tag: 'tfoot',
19645         cn: [
19646         {
19647             tag: 'tr',
19648             cn: [
19649             {
19650                 tag: 'th',
19651                 colspan: '7',
19652                 cls: 'today'
19653             }
19654                     
19655             ]
19656         }
19657         ]
19658     },
19659     
19660     dates:{
19661         en: {
19662             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19663             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19664             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19665             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19666             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19667             today: "Today"
19668         }
19669     },
19670     
19671     modes: [
19672     {
19673         clsName: 'days',
19674         navFnc: 'Month',
19675         navStep: 1
19676     },
19677     {
19678         clsName: 'months',
19679         navFnc: 'FullYear',
19680         navStep: 1
19681     },
19682     {
19683         clsName: 'years',
19684         navFnc: 'FullYear',
19685         navStep: 10
19686     }]
19687 });
19688
19689 Roo.apply(Roo.bootstrap.DateField,  {
19690   
19691     template : {
19692         tag: 'div',
19693         cls: 'datepicker dropdown-menu roo-dynamic',
19694         cn: [
19695         {
19696             tag: 'div',
19697             cls: 'datepicker-days',
19698             cn: [
19699             {
19700                 tag: 'table',
19701                 cls: 'table-condensed',
19702                 cn:[
19703                 Roo.bootstrap.DateField.head,
19704                 {
19705                     tag: 'tbody'
19706                 },
19707                 Roo.bootstrap.DateField.footer
19708                 ]
19709             }
19710             ]
19711         },
19712         {
19713             tag: 'div',
19714             cls: 'datepicker-months',
19715             cn: [
19716             {
19717                 tag: 'table',
19718                 cls: 'table-condensed',
19719                 cn:[
19720                 Roo.bootstrap.DateField.head,
19721                 Roo.bootstrap.DateField.content,
19722                 Roo.bootstrap.DateField.footer
19723                 ]
19724             }
19725             ]
19726         },
19727         {
19728             tag: 'div',
19729             cls: 'datepicker-years',
19730             cn: [
19731             {
19732                 tag: 'table',
19733                 cls: 'table-condensed',
19734                 cn:[
19735                 Roo.bootstrap.DateField.head,
19736                 Roo.bootstrap.DateField.content,
19737                 Roo.bootstrap.DateField.footer
19738                 ]
19739             }
19740             ]
19741         }
19742         ]
19743     }
19744 });
19745
19746  
19747
19748  /*
19749  * - LGPL
19750  *
19751  * TimeField
19752  * 
19753  */
19754
19755 /**
19756  * @class Roo.bootstrap.TimeField
19757  * @extends Roo.bootstrap.Input
19758  * Bootstrap DateField class
19759  * 
19760  * 
19761  * @constructor
19762  * Create a new TimeField
19763  * @param {Object} config The config object
19764  */
19765
19766 Roo.bootstrap.TimeField = function(config){
19767     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19768     this.addEvents({
19769             /**
19770              * @event show
19771              * Fires when this field show.
19772              * @param {Roo.bootstrap.DateField} thisthis
19773              * @param {Mixed} date The date value
19774              */
19775             show : true,
19776             /**
19777              * @event show
19778              * Fires when this field hide.
19779              * @param {Roo.bootstrap.DateField} this
19780              * @param {Mixed} date The date value
19781              */
19782             hide : true,
19783             /**
19784              * @event select
19785              * Fires when select a date.
19786              * @param {Roo.bootstrap.DateField} this
19787              * @param {Mixed} date The date value
19788              */
19789             select : true
19790         });
19791 };
19792
19793 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19794     
19795     /**
19796      * @cfg {String} format
19797      * The default time format string which can be overriden for localization support.  The format must be
19798      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19799      */
19800     format : "H:i",
19801        
19802     onRender: function(ct, position)
19803     {
19804         
19805         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19806                 
19807         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19808         
19809         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19810         
19811         this.pop = this.picker().select('>.datepicker-time',true).first();
19812         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19813         
19814         this.picker().on('mousedown', this.onMousedown, this);
19815         this.picker().on('click', this.onClick, this);
19816         
19817         this.picker().addClass('datepicker-dropdown');
19818     
19819         this.fillTime();
19820         this.update();
19821             
19822         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19823         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19824         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19825         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19826         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19827         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19828
19829     },
19830     
19831     fireKey: function(e){
19832         if (!this.picker().isVisible()){
19833             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19834                 this.show();
19835             }
19836             return;
19837         }
19838
19839         e.preventDefault();
19840         
19841         switch(e.keyCode){
19842             case 27: // escape
19843                 this.hide();
19844                 break;
19845             case 37: // left
19846             case 39: // right
19847                 this.onTogglePeriod();
19848                 break;
19849             case 38: // up
19850                 this.onIncrementMinutes();
19851                 break;
19852             case 40: // down
19853                 this.onDecrementMinutes();
19854                 break;
19855             case 13: // enter
19856             case 9: // tab
19857                 this.setTime();
19858                 break;
19859         }
19860     },
19861     
19862     onClick: function(e) {
19863         e.stopPropagation();
19864         e.preventDefault();
19865     },
19866     
19867     picker : function()
19868     {
19869         return this.el.select('.datepicker', true).first();
19870     },
19871     
19872     fillTime: function()
19873     {    
19874         var time = this.pop.select('tbody', true).first();
19875         
19876         time.dom.innerHTML = '';
19877         
19878         time.createChild({
19879             tag: 'tr',
19880             cn: [
19881                 {
19882                     tag: 'td',
19883                     cn: [
19884                         {
19885                             tag: 'a',
19886                             href: '#',
19887                             cls: 'btn',
19888                             cn: [
19889                                 {
19890                                     tag: 'span',
19891                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19892                                 }
19893                             ]
19894                         } 
19895                     ]
19896                 },
19897                 {
19898                     tag: 'td',
19899                     cls: 'separator'
19900                 },
19901                 {
19902                     tag: 'td',
19903                     cn: [
19904                         {
19905                             tag: 'a',
19906                             href: '#',
19907                             cls: 'btn',
19908                             cn: [
19909                                 {
19910                                     tag: 'span',
19911                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19912                                 }
19913                             ]
19914                         }
19915                     ]
19916                 },
19917                 {
19918                     tag: 'td',
19919                     cls: 'separator'
19920                 }
19921             ]
19922         });
19923         
19924         time.createChild({
19925             tag: 'tr',
19926             cn: [
19927                 {
19928                     tag: 'td',
19929                     cn: [
19930                         {
19931                             tag: 'span',
19932                             cls: 'timepicker-hour',
19933                             html: '00'
19934                         }  
19935                     ]
19936                 },
19937                 {
19938                     tag: 'td',
19939                     cls: 'separator',
19940                     html: ':'
19941                 },
19942                 {
19943                     tag: 'td',
19944                     cn: [
19945                         {
19946                             tag: 'span',
19947                             cls: 'timepicker-minute',
19948                             html: '00'
19949                         }  
19950                     ]
19951                 },
19952                 {
19953                     tag: 'td',
19954                     cls: 'separator'
19955                 },
19956                 {
19957                     tag: 'td',
19958                     cn: [
19959                         {
19960                             tag: 'button',
19961                             type: 'button',
19962                             cls: 'btn btn-primary period',
19963                             html: 'AM'
19964                             
19965                         }
19966                     ]
19967                 }
19968             ]
19969         });
19970         
19971         time.createChild({
19972             tag: 'tr',
19973             cn: [
19974                 {
19975                     tag: 'td',
19976                     cn: [
19977                         {
19978                             tag: 'a',
19979                             href: '#',
19980                             cls: 'btn',
19981                             cn: [
19982                                 {
19983                                     tag: 'span',
19984                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19985                                 }
19986                             ]
19987                         }
19988                     ]
19989                 },
19990                 {
19991                     tag: 'td',
19992                     cls: 'separator'
19993                 },
19994                 {
19995                     tag: 'td',
19996                     cn: [
19997                         {
19998                             tag: 'a',
19999                             href: '#',
20000                             cls: 'btn',
20001                             cn: [
20002                                 {
20003                                     tag: 'span',
20004                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20005                                 }
20006                             ]
20007                         }
20008                     ]
20009                 },
20010                 {
20011                     tag: 'td',
20012                     cls: 'separator'
20013                 }
20014             ]
20015         });
20016         
20017     },
20018     
20019     update: function()
20020     {
20021         
20022         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20023         
20024         this.fill();
20025     },
20026     
20027     fill: function() 
20028     {
20029         var hours = this.time.getHours();
20030         var minutes = this.time.getMinutes();
20031         var period = 'AM';
20032         
20033         if(hours > 11){
20034             period = 'PM';
20035         }
20036         
20037         if(hours == 0){
20038             hours = 12;
20039         }
20040         
20041         
20042         if(hours > 12){
20043             hours = hours - 12;
20044         }
20045         
20046         if(hours < 10){
20047             hours = '0' + hours;
20048         }
20049         
20050         if(minutes < 10){
20051             minutes = '0' + minutes;
20052         }
20053         
20054         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20055         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20056         this.pop.select('button', true).first().dom.innerHTML = period;
20057         
20058     },
20059     
20060     place: function()
20061     {   
20062         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20063         
20064         var cls = ['bottom'];
20065         
20066         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20067             cls.pop();
20068             cls.push('top');
20069         }
20070         
20071         cls.push('right');
20072         
20073         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20074             cls.pop();
20075             cls.push('left');
20076         }
20077         
20078         this.picker().addClass(cls.join('-'));
20079         
20080         var _this = this;
20081         
20082         Roo.each(cls, function(c){
20083             if(c == 'bottom'){
20084                 _this.picker().setTop(_this.inputEl().getHeight());
20085                 return;
20086             }
20087             if(c == 'top'){
20088                 _this.picker().setTop(0 - _this.picker().getHeight());
20089                 return;
20090             }
20091             
20092             if(c == 'left'){
20093                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20094                 return;
20095             }
20096             if(c == 'right'){
20097                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20098                 return;
20099             }
20100         });
20101         
20102     },
20103   
20104     onFocus : function()
20105     {
20106         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20107         this.show();
20108     },
20109     
20110     onBlur : function()
20111     {
20112         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20113         this.hide();
20114     },
20115     
20116     show : function()
20117     {
20118         this.picker().show();
20119         this.pop.show();
20120         this.update();
20121         this.place();
20122         
20123         this.fireEvent('show', this, this.date);
20124     },
20125     
20126     hide : function()
20127     {
20128         this.picker().hide();
20129         this.pop.hide();
20130         
20131         this.fireEvent('hide', this, this.date);
20132     },
20133     
20134     setTime : function()
20135     {
20136         this.hide();
20137         this.setValue(this.time.format(this.format));
20138         
20139         this.fireEvent('select', this, this.date);
20140         
20141         
20142     },
20143     
20144     onMousedown: function(e){
20145         e.stopPropagation();
20146         e.preventDefault();
20147     },
20148     
20149     onIncrementHours: function()
20150     {
20151         Roo.log('onIncrementHours');
20152         this.time = this.time.add(Date.HOUR, 1);
20153         this.update();
20154         
20155     },
20156     
20157     onDecrementHours: function()
20158     {
20159         Roo.log('onDecrementHours');
20160         this.time = this.time.add(Date.HOUR, -1);
20161         this.update();
20162     },
20163     
20164     onIncrementMinutes: function()
20165     {
20166         Roo.log('onIncrementMinutes');
20167         this.time = this.time.add(Date.MINUTE, 1);
20168         this.update();
20169     },
20170     
20171     onDecrementMinutes: function()
20172     {
20173         Roo.log('onDecrementMinutes');
20174         this.time = this.time.add(Date.MINUTE, -1);
20175         this.update();
20176     },
20177     
20178     onTogglePeriod: function()
20179     {
20180         Roo.log('onTogglePeriod');
20181         this.time = this.time.add(Date.HOUR, 12);
20182         this.update();
20183     }
20184     
20185    
20186 });
20187
20188 Roo.apply(Roo.bootstrap.TimeField,  {
20189     
20190     content : {
20191         tag: 'tbody',
20192         cn: [
20193             {
20194                 tag: 'tr',
20195                 cn: [
20196                 {
20197                     tag: 'td',
20198                     colspan: '7'
20199                 }
20200                 ]
20201             }
20202         ]
20203     },
20204     
20205     footer : {
20206         tag: 'tfoot',
20207         cn: [
20208             {
20209                 tag: 'tr',
20210                 cn: [
20211                 {
20212                     tag: 'th',
20213                     colspan: '7',
20214                     cls: '',
20215                     cn: [
20216                         {
20217                             tag: 'button',
20218                             cls: 'btn btn-info ok',
20219                             html: 'OK'
20220                         }
20221                     ]
20222                 }
20223
20224                 ]
20225             }
20226         ]
20227     }
20228 });
20229
20230 Roo.apply(Roo.bootstrap.TimeField,  {
20231   
20232     template : {
20233         tag: 'div',
20234         cls: 'datepicker dropdown-menu',
20235         cn: [
20236             {
20237                 tag: 'div',
20238                 cls: 'datepicker-time',
20239                 cn: [
20240                 {
20241                     tag: 'table',
20242                     cls: 'table-condensed',
20243                     cn:[
20244                     Roo.bootstrap.TimeField.content,
20245                     Roo.bootstrap.TimeField.footer
20246                     ]
20247                 }
20248                 ]
20249             }
20250         ]
20251     }
20252 });
20253
20254  
20255
20256  /*
20257  * - LGPL
20258  *
20259  * MonthField
20260  * 
20261  */
20262
20263 /**
20264  * @class Roo.bootstrap.MonthField
20265  * @extends Roo.bootstrap.Input
20266  * Bootstrap MonthField class
20267  * 
20268  * @cfg {String} language default en
20269  * 
20270  * @constructor
20271  * Create a new MonthField
20272  * @param {Object} config The config object
20273  */
20274
20275 Roo.bootstrap.MonthField = function(config){
20276     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20277     
20278     this.addEvents({
20279         /**
20280          * @event show
20281          * Fires when this field show.
20282          * @param {Roo.bootstrap.MonthField} this
20283          * @param {Mixed} date The date value
20284          */
20285         show : true,
20286         /**
20287          * @event show
20288          * Fires when this field hide.
20289          * @param {Roo.bootstrap.MonthField} this
20290          * @param {Mixed} date The date value
20291          */
20292         hide : true,
20293         /**
20294          * @event select
20295          * Fires when select a date.
20296          * @param {Roo.bootstrap.MonthField} this
20297          * @param {String} oldvalue The old value
20298          * @param {String} newvalue The new value
20299          */
20300         select : true
20301     });
20302 };
20303
20304 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20305     
20306     onRender: function(ct, position)
20307     {
20308         
20309         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20310         
20311         this.language = this.language || 'en';
20312         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20313         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20314         
20315         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20316         this.isInline = false;
20317         this.isInput = true;
20318         this.component = this.el.select('.add-on', true).first() || false;
20319         this.component = (this.component && this.component.length === 0) ? false : this.component;
20320         this.hasInput = this.component && this.inputEL().length;
20321         
20322         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20323         
20324         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20325         
20326         this.picker().on('mousedown', this.onMousedown, this);
20327         this.picker().on('click', this.onClick, this);
20328         
20329         this.picker().addClass('datepicker-dropdown');
20330         
20331         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20332             v.setStyle('width', '189px');
20333         });
20334         
20335         this.fillMonths();
20336         
20337         this.update();
20338         
20339         if(this.isInline) {
20340             this.show();
20341         }
20342         
20343     },
20344     
20345     setValue: function(v, suppressEvent)
20346     {   
20347         var o = this.getValue();
20348         
20349         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20350         
20351         this.update();
20352
20353         if(suppressEvent !== true){
20354             this.fireEvent('select', this, o, v);
20355         }
20356         
20357     },
20358     
20359     getValue: function()
20360     {
20361         return this.value;
20362     },
20363     
20364     onClick: function(e) 
20365     {
20366         e.stopPropagation();
20367         e.preventDefault();
20368         
20369         var target = e.getTarget();
20370         
20371         if(target.nodeName.toLowerCase() === 'i'){
20372             target = Roo.get(target).dom.parentNode;
20373         }
20374         
20375         var nodeName = target.nodeName;
20376         var className = target.className;
20377         var html = target.innerHTML;
20378         
20379         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20380             return;
20381         }
20382         
20383         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20384         
20385         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20386         
20387         this.hide();
20388                         
20389     },
20390     
20391     picker : function()
20392     {
20393         return this.pickerEl;
20394     },
20395     
20396     fillMonths: function()
20397     {    
20398         var i = 0;
20399         var months = this.picker().select('>.datepicker-months td', true).first();
20400         
20401         months.dom.innerHTML = '';
20402         
20403         while (i < 12) {
20404             var month = {
20405                 tag: 'span',
20406                 cls: 'month',
20407                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20408             };
20409             
20410             months.createChild(month);
20411         }
20412         
20413     },
20414     
20415     update: function()
20416     {
20417         var _this = this;
20418         
20419         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20420             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20421         }
20422         
20423         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20424             e.removeClass('active');
20425             
20426             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20427                 e.addClass('active');
20428             }
20429         })
20430     },
20431     
20432     place: function()
20433     {
20434         if(this.isInline) {
20435             return;
20436         }
20437         
20438         this.picker().removeClass(['bottom', 'top']);
20439         
20440         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20441             /*
20442              * place to the top of element!
20443              *
20444              */
20445             
20446             this.picker().addClass('top');
20447             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20448             
20449             return;
20450         }
20451         
20452         this.picker().addClass('bottom');
20453         
20454         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20455     },
20456     
20457     onFocus : function()
20458     {
20459         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20460         this.show();
20461     },
20462     
20463     onBlur : function()
20464     {
20465         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20466         
20467         var d = this.inputEl().getValue();
20468         
20469         this.setValue(d);
20470                 
20471         this.hide();
20472     },
20473     
20474     show : function()
20475     {
20476         this.picker().show();
20477         this.picker().select('>.datepicker-months', true).first().show();
20478         this.update();
20479         this.place();
20480         
20481         this.fireEvent('show', this, this.date);
20482     },
20483     
20484     hide : function()
20485     {
20486         if(this.isInline) {
20487             return;
20488         }
20489         this.picker().hide();
20490         this.fireEvent('hide', this, this.date);
20491         
20492     },
20493     
20494     onMousedown: function(e)
20495     {
20496         e.stopPropagation();
20497         e.preventDefault();
20498     },
20499     
20500     keyup: function(e)
20501     {
20502         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20503         this.update();
20504     },
20505
20506     fireKey: function(e)
20507     {
20508         if (!this.picker().isVisible()){
20509             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20510                 this.show();
20511             }
20512             return;
20513         }
20514         
20515         var dir;
20516         
20517         switch(e.keyCode){
20518             case 27: // escape
20519                 this.hide();
20520                 e.preventDefault();
20521                 break;
20522             case 37: // left
20523             case 39: // right
20524                 dir = e.keyCode == 37 ? -1 : 1;
20525                 
20526                 this.vIndex = this.vIndex + dir;
20527                 
20528                 if(this.vIndex < 0){
20529                     this.vIndex = 0;
20530                 }
20531                 
20532                 if(this.vIndex > 11){
20533                     this.vIndex = 11;
20534                 }
20535                 
20536                 if(isNaN(this.vIndex)){
20537                     this.vIndex = 0;
20538                 }
20539                 
20540                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20541                 
20542                 break;
20543             case 38: // up
20544             case 40: // down
20545                 
20546                 dir = e.keyCode == 38 ? -1 : 1;
20547                 
20548                 this.vIndex = this.vIndex + dir * 4;
20549                 
20550                 if(this.vIndex < 0){
20551                     this.vIndex = 0;
20552                 }
20553                 
20554                 if(this.vIndex > 11){
20555                     this.vIndex = 11;
20556                 }
20557                 
20558                 if(isNaN(this.vIndex)){
20559                     this.vIndex = 0;
20560                 }
20561                 
20562                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20563                 break;
20564                 
20565             case 13: // enter
20566                 
20567                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20568                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20569                 }
20570                 
20571                 this.hide();
20572                 e.preventDefault();
20573                 break;
20574             case 9: // tab
20575                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20576                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20577                 }
20578                 this.hide();
20579                 break;
20580             case 16: // shift
20581             case 17: // ctrl
20582             case 18: // alt
20583                 break;
20584             default :
20585                 this.hide();
20586                 
20587         }
20588     },
20589     
20590     remove: function() 
20591     {
20592         this.picker().remove();
20593     }
20594    
20595 });
20596
20597 Roo.apply(Roo.bootstrap.MonthField,  {
20598     
20599     content : {
20600         tag: 'tbody',
20601         cn: [
20602         {
20603             tag: 'tr',
20604             cn: [
20605             {
20606                 tag: 'td',
20607                 colspan: '7'
20608             }
20609             ]
20610         }
20611         ]
20612     },
20613     
20614     dates:{
20615         en: {
20616             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20617             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20618         }
20619     }
20620 });
20621
20622 Roo.apply(Roo.bootstrap.MonthField,  {
20623   
20624     template : {
20625         tag: 'div',
20626         cls: 'datepicker dropdown-menu roo-dynamic',
20627         cn: [
20628             {
20629                 tag: 'div',
20630                 cls: 'datepicker-months',
20631                 cn: [
20632                 {
20633                     tag: 'table',
20634                     cls: 'table-condensed',
20635                     cn:[
20636                         Roo.bootstrap.DateField.content
20637                     ]
20638                 }
20639                 ]
20640             }
20641         ]
20642     }
20643 });
20644
20645  
20646
20647  
20648  /*
20649  * - LGPL
20650  *
20651  * CheckBox
20652  * 
20653  */
20654
20655 /**
20656  * @class Roo.bootstrap.CheckBox
20657  * @extends Roo.bootstrap.Input
20658  * Bootstrap CheckBox class
20659  * 
20660  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20661  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20662  * @cfg {String} boxLabel The text that appears beside the checkbox
20663  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20664  * @cfg {Boolean} checked initnal the element
20665  * @cfg {Boolean} inline inline the element (default false)
20666  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20667  * @cfg {String} tooltip label tooltip
20668  * 
20669  * @constructor
20670  * Create a new CheckBox
20671  * @param {Object} config The config object
20672  */
20673
20674 Roo.bootstrap.CheckBox = function(config){
20675     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20676    
20677     this.addEvents({
20678         /**
20679         * @event check
20680         * Fires when the element is checked or unchecked.
20681         * @param {Roo.bootstrap.CheckBox} this This input
20682         * @param {Boolean} checked The new checked value
20683         */
20684        check : true,
20685        /**
20686         * @event click
20687         * Fires when the element is click.
20688         * @param {Roo.bootstrap.CheckBox} this This input
20689         */
20690        click : true
20691     });
20692     
20693 };
20694
20695 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20696   
20697     inputType: 'checkbox',
20698     inputValue: 1,
20699     valueOff: 0,
20700     boxLabel: false,
20701     checked: false,
20702     weight : false,
20703     inline: false,
20704     tooltip : '',
20705     
20706     getAutoCreate : function()
20707     {
20708         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20709         
20710         var id = Roo.id();
20711         
20712         var cfg = {};
20713         
20714         cfg.cls = 'form-group ' + this.inputType; //input-group
20715         
20716         if(this.inline){
20717             cfg.cls += ' ' + this.inputType + '-inline';
20718         }
20719         
20720         var input =  {
20721             tag: 'input',
20722             id : id,
20723             type : this.inputType,
20724             value : this.inputValue,
20725             cls : 'roo-' + this.inputType, //'form-box',
20726             placeholder : this.placeholder || ''
20727             
20728         };
20729         
20730         if(this.inputType != 'radio'){
20731             var hidden =  {
20732                 tag: 'input',
20733                 type : 'hidden',
20734                 cls : 'roo-hidden-value',
20735                 value : this.checked ? this.inputValue : this.valueOff
20736             };
20737         }
20738         
20739             
20740         if (this.weight) { // Validity check?
20741             cfg.cls += " " + this.inputType + "-" + this.weight;
20742         }
20743         
20744         if (this.disabled) {
20745             input.disabled=true;
20746         }
20747         
20748         if(this.checked){
20749             input.checked = this.checked;
20750         }
20751         
20752         if (this.name) {
20753             
20754             input.name = this.name;
20755             
20756             if(this.inputType != 'radio'){
20757                 hidden.name = this.name;
20758                 input.name = '_hidden_' + this.name;
20759             }
20760         }
20761         
20762         if (this.size) {
20763             input.cls += ' input-' + this.size;
20764         }
20765         
20766         var settings=this;
20767         
20768         ['xs','sm','md','lg'].map(function(size){
20769             if (settings[size]) {
20770                 cfg.cls += ' col-' + size + '-' + settings[size];
20771             }
20772         });
20773         
20774         var inputblock = input;
20775          
20776         if (this.before || this.after) {
20777             
20778             inputblock = {
20779                 cls : 'input-group',
20780                 cn :  [] 
20781             };
20782             
20783             if (this.before) {
20784                 inputblock.cn.push({
20785                     tag :'span',
20786                     cls : 'input-group-addon',
20787                     html : this.before
20788                 });
20789             }
20790             
20791             inputblock.cn.push(input);
20792             
20793             if(this.inputType != 'radio'){
20794                 inputblock.cn.push(hidden);
20795             }
20796             
20797             if (this.after) {
20798                 inputblock.cn.push({
20799                     tag :'span',
20800                     cls : 'input-group-addon',
20801                     html : this.after
20802                 });
20803             }
20804             
20805         }
20806         
20807         if (align ==='left' && this.fieldLabel.length) {
20808 //                Roo.log("left and has label");
20809             cfg.cn = [
20810                 {
20811                     tag: 'label',
20812                     'for' :  id,
20813                     cls : 'control-label',
20814                     html : this.fieldLabel
20815                 },
20816                 {
20817                     cls : "", 
20818                     cn: [
20819                         inputblock
20820                     ]
20821                 }
20822             ];
20823             
20824             if(this.labelWidth > 12){
20825                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20826             }
20827             
20828             if(this.labelWidth < 13 && this.labelmd == 0){
20829                 this.labelmd = this.labelWidth;
20830             }
20831             
20832             if(this.labellg > 0){
20833                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20834                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20835             }
20836             
20837             if(this.labelmd > 0){
20838                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20839                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20840             }
20841             
20842             if(this.labelsm > 0){
20843                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20844                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20845             }
20846             
20847             if(this.labelxs > 0){
20848                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20849                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20850             }
20851             
20852         } else if ( this.fieldLabel.length) {
20853 //                Roo.log(" label");
20854                 cfg.cn = [
20855                    
20856                     {
20857                         tag: this.boxLabel ? 'span' : 'label',
20858                         'for': id,
20859                         cls: 'control-label box-input-label',
20860                         //cls : 'input-group-addon',
20861                         html : this.fieldLabel
20862                     },
20863                     
20864                     inputblock
20865                     
20866                 ];
20867
20868         } else {
20869             
20870 //                Roo.log(" no label && no align");
20871                 cfg.cn = [  inputblock ] ;
20872                 
20873                 
20874         }
20875         
20876         if(this.boxLabel){
20877              var boxLabelCfg = {
20878                 tag: 'label',
20879                 //'for': id, // box label is handled by onclick - so no for...
20880                 cls: 'box-label',
20881                 html: this.boxLabel
20882             };
20883             
20884             if(this.tooltip){
20885                 boxLabelCfg.tooltip = this.tooltip;
20886             }
20887              
20888             cfg.cn.push(boxLabelCfg);
20889         }
20890         
20891         if(this.inputType != 'radio'){
20892             cfg.cn.push(hidden);
20893         }
20894         
20895         return cfg;
20896         
20897     },
20898     
20899     /**
20900      * return the real input element.
20901      */
20902     inputEl: function ()
20903     {
20904         return this.el.select('input.roo-' + this.inputType,true).first();
20905     },
20906     hiddenEl: function ()
20907     {
20908         return this.el.select('input.roo-hidden-value',true).first();
20909     },
20910     
20911     labelEl: function()
20912     {
20913         return this.el.select('label.control-label',true).first();
20914     },
20915     /* depricated... */
20916     
20917     label: function()
20918     {
20919         return this.labelEl();
20920     },
20921     
20922     boxLabelEl: function()
20923     {
20924         return this.el.select('label.box-label',true).first();
20925     },
20926     
20927     initEvents : function()
20928     {
20929 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20930         
20931         this.inputEl().on('click', this.onClick,  this);
20932         
20933         if (this.boxLabel) { 
20934             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20935         }
20936         
20937         this.startValue = this.getValue();
20938         
20939         if(this.groupId){
20940             Roo.bootstrap.CheckBox.register(this);
20941         }
20942     },
20943     
20944     onClick : function(e)
20945     {   
20946         if(this.fireEvent('click', this, e) !== false){
20947             this.setChecked(!this.checked);
20948         }
20949         
20950     },
20951     
20952     setChecked : function(state,suppressEvent)
20953     {
20954         this.startValue = this.getValue();
20955
20956         if(this.inputType == 'radio'){
20957             
20958             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20959                 e.dom.checked = false;
20960             });
20961             
20962             this.inputEl().dom.checked = true;
20963             
20964             this.inputEl().dom.value = this.inputValue;
20965             
20966             if(suppressEvent !== true){
20967                 this.fireEvent('check', this, true);
20968             }
20969             
20970             this.validate();
20971             
20972             return;
20973         }
20974         
20975         this.checked = state;
20976         
20977         this.inputEl().dom.checked = state;
20978         
20979         
20980         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20981         
20982         if(suppressEvent !== true){
20983             this.fireEvent('check', this, state);
20984         }
20985         
20986         this.validate();
20987     },
20988     
20989     getValue : function()
20990     {
20991         if(this.inputType == 'radio'){
20992             return this.getGroupValue();
20993         }
20994         
20995         return this.hiddenEl().dom.value;
20996         
20997     },
20998     
20999     getGroupValue : function()
21000     {
21001         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21002             return '';
21003         }
21004         
21005         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21006     },
21007     
21008     setValue : function(v,suppressEvent)
21009     {
21010         if(this.inputType == 'radio'){
21011             this.setGroupValue(v, suppressEvent);
21012             return;
21013         }
21014         
21015         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21016         
21017         this.validate();
21018     },
21019     
21020     setGroupValue : function(v, suppressEvent)
21021     {
21022         this.startValue = this.getValue();
21023         
21024         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21025             e.dom.checked = false;
21026             
21027             if(e.dom.value == v){
21028                 e.dom.checked = true;
21029             }
21030         });
21031         
21032         if(suppressEvent !== true){
21033             this.fireEvent('check', this, true);
21034         }
21035
21036         this.validate();
21037         
21038         return;
21039     },
21040     
21041     validate : function()
21042     {
21043         if(this.getVisibilityEl().hasClass('hidden')){
21044             return true;
21045         }
21046         
21047         if(
21048                 this.disabled || 
21049                 (this.inputType == 'radio' && this.validateRadio()) ||
21050                 (this.inputType == 'checkbox' && this.validateCheckbox())
21051         ){
21052             this.markValid();
21053             return true;
21054         }
21055         
21056         this.markInvalid();
21057         return false;
21058     },
21059     
21060     validateRadio : function()
21061     {
21062         if(this.getVisibilityEl().hasClass('hidden')){
21063             return true;
21064         }
21065         
21066         if(this.allowBlank){
21067             return true;
21068         }
21069         
21070         var valid = false;
21071         
21072         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21073             if(!e.dom.checked){
21074                 return;
21075             }
21076             
21077             valid = true;
21078             
21079             return false;
21080         });
21081         
21082         return valid;
21083     },
21084     
21085     validateCheckbox : function()
21086     {
21087         if(!this.groupId){
21088             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21089             //return (this.getValue() == this.inputValue) ? true : false;
21090         }
21091         
21092         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21093         
21094         if(!group){
21095             return false;
21096         }
21097         
21098         var r = false;
21099         
21100         for(var i in group){
21101             if(group[i].el.isVisible(true)){
21102                 r = false;
21103                 break;
21104             }
21105             
21106             r = true;
21107         }
21108         
21109         for(var i in group){
21110             if(r){
21111                 break;
21112             }
21113             
21114             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21115         }
21116         
21117         return r;
21118     },
21119     
21120     /**
21121      * Mark this field as valid
21122      */
21123     markValid : function()
21124     {
21125         var _this = this;
21126         
21127         this.fireEvent('valid', this);
21128         
21129         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21130         
21131         if(this.groupId){
21132             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21133         }
21134         
21135         if(label){
21136             label.markValid();
21137         }
21138
21139         if(this.inputType == 'radio'){
21140             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21141                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21142                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21143             });
21144             
21145             return;
21146         }
21147
21148         if(!this.groupId){
21149             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21150             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21151             return;
21152         }
21153         
21154         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21155         
21156         if(!group){
21157             return;
21158         }
21159         
21160         for(var i in group){
21161             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21162             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21163         }
21164     },
21165     
21166      /**
21167      * Mark this field as invalid
21168      * @param {String} msg The validation message
21169      */
21170     markInvalid : function(msg)
21171     {
21172         if(this.allowBlank){
21173             return;
21174         }
21175         
21176         var _this = this;
21177         
21178         this.fireEvent('invalid', this, msg);
21179         
21180         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21181         
21182         if(this.groupId){
21183             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21184         }
21185         
21186         if(label){
21187             label.markInvalid();
21188         }
21189             
21190         if(this.inputType == 'radio'){
21191             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21192                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21193                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21194             });
21195             
21196             return;
21197         }
21198         
21199         if(!this.groupId){
21200             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21201             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21202             return;
21203         }
21204         
21205         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21206         
21207         if(!group){
21208             return;
21209         }
21210         
21211         for(var i in group){
21212             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21213             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21214         }
21215         
21216     },
21217     
21218     clearInvalid : function()
21219     {
21220         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21221         
21222         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21223         
21224         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21225         
21226         if (label && label.iconEl) {
21227             label.iconEl.removeClass(label.validClass);
21228             label.iconEl.removeClass(label.invalidClass);
21229         }
21230     },
21231     
21232     disable : function()
21233     {
21234         if(this.inputType != 'radio'){
21235             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21236             return;
21237         }
21238         
21239         var _this = this;
21240         
21241         if(this.rendered){
21242             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21243                 _this.getActionEl().addClass(this.disabledClass);
21244                 e.dom.disabled = true;
21245             });
21246         }
21247         
21248         this.disabled = true;
21249         this.fireEvent("disable", this);
21250         return this;
21251     },
21252
21253     enable : function()
21254     {
21255         if(this.inputType != 'radio'){
21256             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21257             return;
21258         }
21259         
21260         var _this = this;
21261         
21262         if(this.rendered){
21263             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21264                 _this.getActionEl().removeClass(this.disabledClass);
21265                 e.dom.disabled = false;
21266             });
21267         }
21268         
21269         this.disabled = false;
21270         this.fireEvent("enable", this);
21271         return this;
21272     },
21273     
21274     setBoxLabel : function(v)
21275     {
21276         this.boxLabel = v;
21277         
21278         if(this.rendered){
21279             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21280         }
21281     }
21282
21283 });
21284
21285 Roo.apply(Roo.bootstrap.CheckBox, {
21286     
21287     groups: {},
21288     
21289      /**
21290     * register a CheckBox Group
21291     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21292     */
21293     register : function(checkbox)
21294     {
21295         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21296             this.groups[checkbox.groupId] = {};
21297         }
21298         
21299         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21300             return;
21301         }
21302         
21303         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21304         
21305     },
21306     /**
21307     * fetch a CheckBox Group based on the group ID
21308     * @param {string} the group ID
21309     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21310     */
21311     get: function(groupId) {
21312         if (typeof(this.groups[groupId]) == 'undefined') {
21313             return false;
21314         }
21315         
21316         return this.groups[groupId] ;
21317     }
21318     
21319     
21320 });
21321 /*
21322  * - LGPL
21323  *
21324  * RadioItem
21325  * 
21326  */
21327
21328 /**
21329  * @class Roo.bootstrap.Radio
21330  * @extends Roo.bootstrap.Component
21331  * Bootstrap Radio class
21332  * @cfg {String} boxLabel - the label associated
21333  * @cfg {String} value - the value of radio
21334  * 
21335  * @constructor
21336  * Create a new Radio
21337  * @param {Object} config The config object
21338  */
21339 Roo.bootstrap.Radio = function(config){
21340     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21341     
21342 };
21343
21344 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21345     
21346     boxLabel : '',
21347     
21348     value : '',
21349     
21350     getAutoCreate : function()
21351     {
21352         var cfg = {
21353             tag : 'div',
21354             cls : 'form-group radio',
21355             cn : [
21356                 {
21357                     tag : 'label',
21358                     cls : 'box-label',
21359                     html : this.boxLabel
21360                 }
21361             ]
21362         };
21363         
21364         return cfg;
21365     },
21366     
21367     initEvents : function() 
21368     {
21369         this.parent().register(this);
21370         
21371         this.el.on('click', this.onClick, this);
21372         
21373     },
21374     
21375     onClick : function(e)
21376     {
21377         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21378             this.setChecked(true);
21379         }
21380     },
21381     
21382     setChecked : function(state, suppressEvent)
21383     {
21384         this.parent().setValue(this.value, suppressEvent);
21385         
21386     },
21387     
21388     setBoxLabel : function(v)
21389     {
21390         this.boxLabel = v;
21391         
21392         if(this.rendered){
21393             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21394         }
21395     }
21396     
21397 });
21398  
21399
21400  /*
21401  * - LGPL
21402  *
21403  * Input
21404  * 
21405  */
21406
21407 /**
21408  * @class Roo.bootstrap.SecurePass
21409  * @extends Roo.bootstrap.Input
21410  * Bootstrap SecurePass class
21411  *
21412  * 
21413  * @constructor
21414  * Create a new SecurePass
21415  * @param {Object} config The config object
21416  */
21417  
21418 Roo.bootstrap.SecurePass = function (config) {
21419     // these go here, so the translation tool can replace them..
21420     this.errors = {
21421         PwdEmpty: "Please type a password, and then retype it to confirm.",
21422         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21423         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21424         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21425         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21426         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21427         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21428         TooWeak: "Your password is Too Weak."
21429     },
21430     this.meterLabel = "Password strength:";
21431     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21432     this.meterClass = [
21433         "roo-password-meter-tooweak", 
21434         "roo-password-meter-weak", 
21435         "roo-password-meter-medium", 
21436         "roo-password-meter-strong", 
21437         "roo-password-meter-grey"
21438     ];
21439     
21440     this.errors = {};
21441     
21442     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21443 }
21444
21445 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21446     /**
21447      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21448      * {
21449      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21450      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21451      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21452      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21453      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21454      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21455      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21456      * })
21457      */
21458     // private
21459     
21460     meterWidth: 300,
21461     errorMsg :'',    
21462     errors: false,
21463     imageRoot: '/',
21464     /**
21465      * @cfg {String/Object} Label for the strength meter (defaults to
21466      * 'Password strength:')
21467      */
21468     // private
21469     meterLabel: '',
21470     /**
21471      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21472      * ['Weak', 'Medium', 'Strong'])
21473      */
21474     // private    
21475     pwdStrengths: false,    
21476     // private
21477     strength: 0,
21478     // private
21479     _lastPwd: null,
21480     // private
21481     kCapitalLetter: 0,
21482     kSmallLetter: 1,
21483     kDigit: 2,
21484     kPunctuation: 3,
21485     
21486     insecure: false,
21487     // private
21488     initEvents: function ()
21489     {
21490         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21491
21492         if (this.el.is('input[type=password]') && Roo.isSafari) {
21493             this.el.on('keydown', this.SafariOnKeyDown, this);
21494         }
21495
21496         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21497     },
21498     // private
21499     onRender: function (ct, position)
21500     {
21501         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21502         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21503         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21504
21505         this.trigger.createChild({
21506                    cn: [
21507                     {
21508                     //id: 'PwdMeter',
21509                     tag: 'div',
21510                     cls: 'roo-password-meter-grey col-xs-12',
21511                     style: {
21512                         //width: 0,
21513                         //width: this.meterWidth + 'px'                                                
21514                         }
21515                     },
21516                     {                            
21517                          cls: 'roo-password-meter-text'                          
21518                     }
21519                 ]            
21520         });
21521
21522          
21523         if (this.hideTrigger) {
21524             this.trigger.setDisplayed(false);
21525         }
21526         this.setSize(this.width || '', this.height || '');
21527     },
21528     // private
21529     onDestroy: function ()
21530     {
21531         if (this.trigger) {
21532             this.trigger.removeAllListeners();
21533             this.trigger.remove();
21534         }
21535         if (this.wrap) {
21536             this.wrap.remove();
21537         }
21538         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21539     },
21540     // private
21541     checkStrength: function ()
21542     {
21543         var pwd = this.inputEl().getValue();
21544         if (pwd == this._lastPwd) {
21545             return;
21546         }
21547
21548         var strength;
21549         if (this.ClientSideStrongPassword(pwd)) {
21550             strength = 3;
21551         } else if (this.ClientSideMediumPassword(pwd)) {
21552             strength = 2;
21553         } else if (this.ClientSideWeakPassword(pwd)) {
21554             strength = 1;
21555         } else {
21556             strength = 0;
21557         }
21558         
21559         Roo.log('strength1: ' + strength);
21560         
21561         //var pm = this.trigger.child('div/div/div').dom;
21562         var pm = this.trigger.child('div/div');
21563         pm.removeClass(this.meterClass);
21564         pm.addClass(this.meterClass[strength]);
21565                 
21566         
21567         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21568                 
21569         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21570         
21571         this._lastPwd = pwd;
21572     },
21573     reset: function ()
21574     {
21575         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21576         
21577         this._lastPwd = '';
21578         
21579         var pm = this.trigger.child('div/div');
21580         pm.removeClass(this.meterClass);
21581         pm.addClass('roo-password-meter-grey');        
21582         
21583         
21584         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21585         
21586         pt.innerHTML = '';
21587         this.inputEl().dom.type='password';
21588     },
21589     // private
21590     validateValue: function (value)
21591     {
21592         
21593         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21594             return false;
21595         }
21596         if (value.length == 0) {
21597             if (this.allowBlank) {
21598                 this.clearInvalid();
21599                 return true;
21600             }
21601
21602             this.markInvalid(this.errors.PwdEmpty);
21603             this.errorMsg = this.errors.PwdEmpty;
21604             return false;
21605         }
21606         
21607         if(this.insecure){
21608             return true;
21609         }
21610         
21611         if ('[\x21-\x7e]*'.match(value)) {
21612             this.markInvalid(this.errors.PwdBadChar);
21613             this.errorMsg = this.errors.PwdBadChar;
21614             return false;
21615         }
21616         if (value.length < 6) {
21617             this.markInvalid(this.errors.PwdShort);
21618             this.errorMsg = this.errors.PwdShort;
21619             return false;
21620         }
21621         if (value.length > 16) {
21622             this.markInvalid(this.errors.PwdLong);
21623             this.errorMsg = this.errors.PwdLong;
21624             return false;
21625         }
21626         var strength;
21627         if (this.ClientSideStrongPassword(value)) {
21628             strength = 3;
21629         } else if (this.ClientSideMediumPassword(value)) {
21630             strength = 2;
21631         } else if (this.ClientSideWeakPassword(value)) {
21632             strength = 1;
21633         } else {
21634             strength = 0;
21635         }
21636
21637         
21638         if (strength < 2) {
21639             //this.markInvalid(this.errors.TooWeak);
21640             this.errorMsg = this.errors.TooWeak;
21641             //return false;
21642         }
21643         
21644         
21645         console.log('strength2: ' + strength);
21646         
21647         //var pm = this.trigger.child('div/div/div').dom;
21648         
21649         var pm = this.trigger.child('div/div');
21650         pm.removeClass(this.meterClass);
21651         pm.addClass(this.meterClass[strength]);
21652                 
21653         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21654                 
21655         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21656         
21657         this.errorMsg = ''; 
21658         return true;
21659     },
21660     // private
21661     CharacterSetChecks: function (type)
21662     {
21663         this.type = type;
21664         this.fResult = false;
21665     },
21666     // private
21667     isctype: function (character, type)
21668     {
21669         switch (type) {  
21670             case this.kCapitalLetter:
21671                 if (character >= 'A' && character <= 'Z') {
21672                     return true;
21673                 }
21674                 break;
21675             
21676             case this.kSmallLetter:
21677                 if (character >= 'a' && character <= 'z') {
21678                     return true;
21679                 }
21680                 break;
21681             
21682             case this.kDigit:
21683                 if (character >= '0' && character <= '9') {
21684                     return true;
21685                 }
21686                 break;
21687             
21688             case this.kPunctuation:
21689                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21690                     return true;
21691                 }
21692                 break;
21693             
21694             default:
21695                 return false;
21696         }
21697
21698     },
21699     // private
21700     IsLongEnough: function (pwd, size)
21701     {
21702         return !(pwd == null || isNaN(size) || pwd.length < size);
21703     },
21704     // private
21705     SpansEnoughCharacterSets: function (word, nb)
21706     {
21707         if (!this.IsLongEnough(word, nb))
21708         {
21709             return false;
21710         }
21711
21712         var characterSetChecks = new Array(
21713             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21714             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21715         );
21716         
21717         for (var index = 0; index < word.length; ++index) {
21718             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21719                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21720                     characterSetChecks[nCharSet].fResult = true;
21721                     break;
21722                 }
21723             }
21724         }
21725
21726         var nCharSets = 0;
21727         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21728             if (characterSetChecks[nCharSet].fResult) {
21729                 ++nCharSets;
21730             }
21731         }
21732
21733         if (nCharSets < nb) {
21734             return false;
21735         }
21736         return true;
21737     },
21738     // private
21739     ClientSideStrongPassword: function (pwd)
21740     {
21741         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21742     },
21743     // private
21744     ClientSideMediumPassword: function (pwd)
21745     {
21746         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21747     },
21748     // private
21749     ClientSideWeakPassword: function (pwd)
21750     {
21751         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21752     }
21753           
21754 })//<script type="text/javascript">
21755
21756 /*
21757  * Based  Ext JS Library 1.1.1
21758  * Copyright(c) 2006-2007, Ext JS, LLC.
21759  * LGPL
21760  *
21761  */
21762  
21763 /**
21764  * @class Roo.HtmlEditorCore
21765  * @extends Roo.Component
21766  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21767  *
21768  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21769  */
21770
21771 Roo.HtmlEditorCore = function(config){
21772     
21773     
21774     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21775     
21776     
21777     this.addEvents({
21778         /**
21779          * @event initialize
21780          * Fires when the editor is fully initialized (including the iframe)
21781          * @param {Roo.HtmlEditorCore} this
21782          */
21783         initialize: true,
21784         /**
21785          * @event activate
21786          * Fires when the editor is first receives the focus. Any insertion must wait
21787          * until after this event.
21788          * @param {Roo.HtmlEditorCore} this
21789          */
21790         activate: true,
21791          /**
21792          * @event beforesync
21793          * Fires before the textarea is updated with content from the editor iframe. Return false
21794          * to cancel the sync.
21795          * @param {Roo.HtmlEditorCore} this
21796          * @param {String} html
21797          */
21798         beforesync: true,
21799          /**
21800          * @event beforepush
21801          * Fires before the iframe editor is updated with content from the textarea. Return false
21802          * to cancel the push.
21803          * @param {Roo.HtmlEditorCore} this
21804          * @param {String} html
21805          */
21806         beforepush: true,
21807          /**
21808          * @event sync
21809          * Fires when the textarea is updated with content from the editor iframe.
21810          * @param {Roo.HtmlEditorCore} this
21811          * @param {String} html
21812          */
21813         sync: true,
21814          /**
21815          * @event push
21816          * Fires when the iframe editor is updated with content from the textarea.
21817          * @param {Roo.HtmlEditorCore} this
21818          * @param {String} html
21819          */
21820         push: true,
21821         
21822         /**
21823          * @event editorevent
21824          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21825          * @param {Roo.HtmlEditorCore} this
21826          */
21827         editorevent: true
21828         
21829     });
21830     
21831     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21832     
21833     // defaults : white / black...
21834     this.applyBlacklists();
21835     
21836     
21837     
21838 };
21839
21840
21841 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21842
21843
21844      /**
21845      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21846      */
21847     
21848     owner : false,
21849     
21850      /**
21851      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21852      *                        Roo.resizable.
21853      */
21854     resizable : false,
21855      /**
21856      * @cfg {Number} height (in pixels)
21857      */   
21858     height: 300,
21859    /**
21860      * @cfg {Number} width (in pixels)
21861      */   
21862     width: 500,
21863     
21864     /**
21865      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21866      * 
21867      */
21868     stylesheets: false,
21869     
21870     // id of frame..
21871     frameId: false,
21872     
21873     // private properties
21874     validationEvent : false,
21875     deferHeight: true,
21876     initialized : false,
21877     activated : false,
21878     sourceEditMode : false,
21879     onFocus : Roo.emptyFn,
21880     iframePad:3,
21881     hideMode:'offsets',
21882     
21883     clearUp: true,
21884     
21885     // blacklist + whitelisted elements..
21886     black: false,
21887     white: false,
21888      
21889     bodyCls : '',
21890
21891     /**
21892      * Protected method that will not generally be called directly. It
21893      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21894      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21895      */
21896     getDocMarkup : function(){
21897         // body styles..
21898         var st = '';
21899         
21900         // inherit styels from page...?? 
21901         if (this.stylesheets === false) {
21902             
21903             Roo.get(document.head).select('style').each(function(node) {
21904                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21905             });
21906             
21907             Roo.get(document.head).select('link').each(function(node) { 
21908                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21909             });
21910             
21911         } else if (!this.stylesheets.length) {
21912                 // simple..
21913                 st = '<style type="text/css">' +
21914                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21915                    '</style>';
21916         } else { 
21917             st = '<style type="text/css">' +
21918                     this.stylesheets +
21919                 '</style>';
21920         }
21921         
21922         st +=  '<style type="text/css">' +
21923             'IMG { cursor: pointer } ' +
21924         '</style>';
21925
21926         var cls = 'roo-htmleditor-body';
21927         
21928         if(this.bodyCls.length){
21929             cls += ' ' + this.bodyCls;
21930         }
21931         
21932         return '<html><head>' + st  +
21933             //<style type="text/css">' +
21934             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21935             //'</style>' +
21936             ' </head><body class="' +  cls + '"></body></html>';
21937     },
21938
21939     // private
21940     onRender : function(ct, position)
21941     {
21942         var _t = this;
21943         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21944         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21945         
21946         
21947         this.el.dom.style.border = '0 none';
21948         this.el.dom.setAttribute('tabIndex', -1);
21949         this.el.addClass('x-hidden hide');
21950         
21951         
21952         
21953         if(Roo.isIE){ // fix IE 1px bogus margin
21954             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21955         }
21956        
21957         
21958         this.frameId = Roo.id();
21959         
21960          
21961         
21962         var iframe = this.owner.wrap.createChild({
21963             tag: 'iframe',
21964             cls: 'form-control', // bootstrap..
21965             id: this.frameId,
21966             name: this.frameId,
21967             frameBorder : 'no',
21968             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21969         }, this.el
21970         );
21971         
21972         
21973         this.iframe = iframe.dom;
21974
21975          this.assignDocWin();
21976         
21977         this.doc.designMode = 'on';
21978        
21979         this.doc.open();
21980         this.doc.write(this.getDocMarkup());
21981         this.doc.close();
21982
21983         
21984         var task = { // must defer to wait for browser to be ready
21985             run : function(){
21986                 //console.log("run task?" + this.doc.readyState);
21987                 this.assignDocWin();
21988                 if(this.doc.body || this.doc.readyState == 'complete'){
21989                     try {
21990                         this.doc.designMode="on";
21991                     } catch (e) {
21992                         return;
21993                     }
21994                     Roo.TaskMgr.stop(task);
21995                     this.initEditor.defer(10, this);
21996                 }
21997             },
21998             interval : 10,
21999             duration: 10000,
22000             scope: this
22001         };
22002         Roo.TaskMgr.start(task);
22003
22004     },
22005
22006     // private
22007     onResize : function(w, h)
22008     {
22009          Roo.log('resize: ' +w + ',' + h );
22010         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22011         if(!this.iframe){
22012             return;
22013         }
22014         if(typeof w == 'number'){
22015             
22016             this.iframe.style.width = w + 'px';
22017         }
22018         if(typeof h == 'number'){
22019             
22020             this.iframe.style.height = h + 'px';
22021             if(this.doc){
22022                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22023             }
22024         }
22025         
22026     },
22027
22028     /**
22029      * Toggles the editor between standard and source edit mode.
22030      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22031      */
22032     toggleSourceEdit : function(sourceEditMode){
22033         
22034         this.sourceEditMode = sourceEditMode === true;
22035         
22036         if(this.sourceEditMode){
22037  
22038             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22039             
22040         }else{
22041             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22042             //this.iframe.className = '';
22043             this.deferFocus();
22044         }
22045         //this.setSize(this.owner.wrap.getSize());
22046         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22047     },
22048
22049     
22050   
22051
22052     /**
22053      * Protected method that will not generally be called directly. If you need/want
22054      * custom HTML cleanup, this is the method you should override.
22055      * @param {String} html The HTML to be cleaned
22056      * return {String} The cleaned HTML
22057      */
22058     cleanHtml : function(html){
22059         html = String(html);
22060         if(html.length > 5){
22061             if(Roo.isSafari){ // strip safari nonsense
22062                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22063             }
22064         }
22065         if(html == '&nbsp;'){
22066             html = '';
22067         }
22068         return html;
22069     },
22070
22071     /**
22072      * HTML Editor -> Textarea
22073      * Protected method that will not generally be called directly. Syncs the contents
22074      * of the editor iframe with the textarea.
22075      */
22076     syncValue : function(){
22077         if(this.initialized){
22078             var bd = (this.doc.body || this.doc.documentElement);
22079             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22080             var html = bd.innerHTML;
22081             if(Roo.isSafari){
22082                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22083                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22084                 if(m && m[1]){
22085                     html = '<div style="'+m[0]+'">' + html + '</div>';
22086                 }
22087             }
22088             html = this.cleanHtml(html);
22089             // fix up the special chars.. normaly like back quotes in word...
22090             // however we do not want to do this with chinese..
22091             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22092                 var cc = b.charCodeAt();
22093                 if (
22094                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22095                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22096                     (cc >= 0xf900 && cc < 0xfb00 )
22097                 ) {
22098                         return b;
22099                 }
22100                 return "&#"+cc+";" 
22101             });
22102             if(this.owner.fireEvent('beforesync', this, html) !== false){
22103                 this.el.dom.value = html;
22104                 this.owner.fireEvent('sync', this, html);
22105             }
22106         }
22107     },
22108
22109     /**
22110      * Protected method that will not generally be called directly. Pushes the value of the textarea
22111      * into the iframe editor.
22112      */
22113     pushValue : function(){
22114         if(this.initialized){
22115             var v = this.el.dom.value.trim();
22116             
22117 //            if(v.length < 1){
22118 //                v = '&#160;';
22119 //            }
22120             
22121             if(this.owner.fireEvent('beforepush', this, v) !== false){
22122                 var d = (this.doc.body || this.doc.documentElement);
22123                 d.innerHTML = v;
22124                 this.cleanUpPaste();
22125                 this.el.dom.value = d.innerHTML;
22126                 this.owner.fireEvent('push', this, v);
22127             }
22128         }
22129     },
22130
22131     // private
22132     deferFocus : function(){
22133         this.focus.defer(10, this);
22134     },
22135
22136     // doc'ed in Field
22137     focus : function(){
22138         if(this.win && !this.sourceEditMode){
22139             this.win.focus();
22140         }else{
22141             this.el.focus();
22142         }
22143     },
22144     
22145     assignDocWin: function()
22146     {
22147         var iframe = this.iframe;
22148         
22149          if(Roo.isIE){
22150             this.doc = iframe.contentWindow.document;
22151             this.win = iframe.contentWindow;
22152         } else {
22153 //            if (!Roo.get(this.frameId)) {
22154 //                return;
22155 //            }
22156 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22157 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22158             
22159             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22160                 return;
22161             }
22162             
22163             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22164             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22165         }
22166     },
22167     
22168     // private
22169     initEditor : function(){
22170         //console.log("INIT EDITOR");
22171         this.assignDocWin();
22172         
22173         
22174         
22175         this.doc.designMode="on";
22176         this.doc.open();
22177         this.doc.write(this.getDocMarkup());
22178         this.doc.close();
22179         
22180         var dbody = (this.doc.body || this.doc.documentElement);
22181         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22182         // this copies styles from the containing element into thsi one..
22183         // not sure why we need all of this..
22184         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22185         
22186         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22187         //ss['background-attachment'] = 'fixed'; // w3c
22188         dbody.bgProperties = 'fixed'; // ie
22189         //Roo.DomHelper.applyStyles(dbody, ss);
22190         Roo.EventManager.on(this.doc, {
22191             //'mousedown': this.onEditorEvent,
22192             'mouseup': this.onEditorEvent,
22193             'dblclick': this.onEditorEvent,
22194             'click': this.onEditorEvent,
22195             'keyup': this.onEditorEvent,
22196             buffer:100,
22197             scope: this
22198         });
22199         if(Roo.isGecko){
22200             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22201         }
22202         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22203             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22204         }
22205         this.initialized = true;
22206
22207         this.owner.fireEvent('initialize', this);
22208         this.pushValue();
22209     },
22210
22211     // private
22212     onDestroy : function(){
22213         
22214         
22215         
22216         if(this.rendered){
22217             
22218             //for (var i =0; i < this.toolbars.length;i++) {
22219             //    // fixme - ask toolbars for heights?
22220             //    this.toolbars[i].onDestroy();
22221            // }
22222             
22223             //this.wrap.dom.innerHTML = '';
22224             //this.wrap.remove();
22225         }
22226     },
22227
22228     // private
22229     onFirstFocus : function(){
22230         
22231         this.assignDocWin();
22232         
22233         
22234         this.activated = true;
22235          
22236     
22237         if(Roo.isGecko){ // prevent silly gecko errors
22238             this.win.focus();
22239             var s = this.win.getSelection();
22240             if(!s.focusNode || s.focusNode.nodeType != 3){
22241                 var r = s.getRangeAt(0);
22242                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22243                 r.collapse(true);
22244                 this.deferFocus();
22245             }
22246             try{
22247                 this.execCmd('useCSS', true);
22248                 this.execCmd('styleWithCSS', false);
22249             }catch(e){}
22250         }
22251         this.owner.fireEvent('activate', this);
22252     },
22253
22254     // private
22255     adjustFont: function(btn){
22256         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22257         //if(Roo.isSafari){ // safari
22258         //    adjust *= 2;
22259        // }
22260         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22261         if(Roo.isSafari){ // safari
22262             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22263             v =  (v < 10) ? 10 : v;
22264             v =  (v > 48) ? 48 : v;
22265             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22266             
22267         }
22268         
22269         
22270         v = Math.max(1, v+adjust);
22271         
22272         this.execCmd('FontSize', v  );
22273     },
22274
22275     onEditorEvent : function(e)
22276     {
22277         this.owner.fireEvent('editorevent', this, e);
22278       //  this.updateToolbar();
22279         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22280     },
22281
22282     insertTag : function(tg)
22283     {
22284         // could be a bit smarter... -> wrap the current selected tRoo..
22285         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22286             
22287             range = this.createRange(this.getSelection());
22288             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22289             wrappingNode.appendChild(range.extractContents());
22290             range.insertNode(wrappingNode);
22291
22292             return;
22293             
22294             
22295             
22296         }
22297         this.execCmd("formatblock",   tg);
22298         
22299     },
22300     
22301     insertText : function(txt)
22302     {
22303         
22304         
22305         var range = this.createRange();
22306         range.deleteContents();
22307                //alert(Sender.getAttribute('label'));
22308                
22309         range.insertNode(this.doc.createTextNode(txt));
22310     } ,
22311     
22312      
22313
22314     /**
22315      * Executes a Midas editor command on the editor document and performs necessary focus and
22316      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22317      * @param {String} cmd The Midas command
22318      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22319      */
22320     relayCmd : function(cmd, value){
22321         this.win.focus();
22322         this.execCmd(cmd, value);
22323         this.owner.fireEvent('editorevent', this);
22324         //this.updateToolbar();
22325         this.owner.deferFocus();
22326     },
22327
22328     /**
22329      * Executes a Midas editor command directly on the editor document.
22330      * For visual commands, you should use {@link #relayCmd} instead.
22331      * <b>This should only be called after the editor is initialized.</b>
22332      * @param {String} cmd The Midas command
22333      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22334      */
22335     execCmd : function(cmd, value){
22336         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22337         this.syncValue();
22338     },
22339  
22340  
22341    
22342     /**
22343      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22344      * to insert tRoo.
22345      * @param {String} text | dom node.. 
22346      */
22347     insertAtCursor : function(text)
22348     {
22349         
22350         if(!this.activated){
22351             return;
22352         }
22353         /*
22354         if(Roo.isIE){
22355             this.win.focus();
22356             var r = this.doc.selection.createRange();
22357             if(r){
22358                 r.collapse(true);
22359                 r.pasteHTML(text);
22360                 this.syncValue();
22361                 this.deferFocus();
22362             
22363             }
22364             return;
22365         }
22366         */
22367         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22368             this.win.focus();
22369             
22370             
22371             // from jquery ui (MIT licenced)
22372             var range, node;
22373             var win = this.win;
22374             
22375             if (win.getSelection && win.getSelection().getRangeAt) {
22376                 range = win.getSelection().getRangeAt(0);
22377                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22378                 range.insertNode(node);
22379             } else if (win.document.selection && win.document.selection.createRange) {
22380                 // no firefox support
22381                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22382                 win.document.selection.createRange().pasteHTML(txt);
22383             } else {
22384                 // no firefox support
22385                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22386                 this.execCmd('InsertHTML', txt);
22387             } 
22388             
22389             this.syncValue();
22390             
22391             this.deferFocus();
22392         }
22393     },
22394  // private
22395     mozKeyPress : function(e){
22396         if(e.ctrlKey){
22397             var c = e.getCharCode(), cmd;
22398           
22399             if(c > 0){
22400                 c = String.fromCharCode(c).toLowerCase();
22401                 switch(c){
22402                     case 'b':
22403                         cmd = 'bold';
22404                         break;
22405                     case 'i':
22406                         cmd = 'italic';
22407                         break;
22408                     
22409                     case 'u':
22410                         cmd = 'underline';
22411                         break;
22412                     
22413                     case 'v':
22414                         this.cleanUpPaste.defer(100, this);
22415                         return;
22416                         
22417                 }
22418                 if(cmd){
22419                     this.win.focus();
22420                     this.execCmd(cmd);
22421                     this.deferFocus();
22422                     e.preventDefault();
22423                 }
22424                 
22425             }
22426         }
22427     },
22428
22429     // private
22430     fixKeys : function(){ // load time branching for fastest keydown performance
22431         if(Roo.isIE){
22432             return function(e){
22433                 var k = e.getKey(), r;
22434                 if(k == e.TAB){
22435                     e.stopEvent();
22436                     r = this.doc.selection.createRange();
22437                     if(r){
22438                         r.collapse(true);
22439                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22440                         this.deferFocus();
22441                     }
22442                     return;
22443                 }
22444                 
22445                 if(k == e.ENTER){
22446                     r = this.doc.selection.createRange();
22447                     if(r){
22448                         var target = r.parentElement();
22449                         if(!target || target.tagName.toLowerCase() != 'li'){
22450                             e.stopEvent();
22451                             r.pasteHTML('<br />');
22452                             r.collapse(false);
22453                             r.select();
22454                         }
22455                     }
22456                 }
22457                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22458                     this.cleanUpPaste.defer(100, this);
22459                     return;
22460                 }
22461                 
22462                 
22463             };
22464         }else if(Roo.isOpera){
22465             return function(e){
22466                 var k = e.getKey();
22467                 if(k == e.TAB){
22468                     e.stopEvent();
22469                     this.win.focus();
22470                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22471                     this.deferFocus();
22472                 }
22473                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22474                     this.cleanUpPaste.defer(100, this);
22475                     return;
22476                 }
22477                 
22478             };
22479         }else if(Roo.isSafari){
22480             return function(e){
22481                 var k = e.getKey();
22482                 
22483                 if(k == e.TAB){
22484                     e.stopEvent();
22485                     this.execCmd('InsertText','\t');
22486                     this.deferFocus();
22487                     return;
22488                 }
22489                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22490                     this.cleanUpPaste.defer(100, this);
22491                     return;
22492                 }
22493                 
22494              };
22495         }
22496     }(),
22497     
22498     getAllAncestors: function()
22499     {
22500         var p = this.getSelectedNode();
22501         var a = [];
22502         if (!p) {
22503             a.push(p); // push blank onto stack..
22504             p = this.getParentElement();
22505         }
22506         
22507         
22508         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22509             a.push(p);
22510             p = p.parentNode;
22511         }
22512         a.push(this.doc.body);
22513         return a;
22514     },
22515     lastSel : false,
22516     lastSelNode : false,
22517     
22518     
22519     getSelection : function() 
22520     {
22521         this.assignDocWin();
22522         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22523     },
22524     
22525     getSelectedNode: function() 
22526     {
22527         // this may only work on Gecko!!!
22528         
22529         // should we cache this!!!!
22530         
22531         
22532         
22533          
22534         var range = this.createRange(this.getSelection()).cloneRange();
22535         
22536         if (Roo.isIE) {
22537             var parent = range.parentElement();
22538             while (true) {
22539                 var testRange = range.duplicate();
22540                 testRange.moveToElementText(parent);
22541                 if (testRange.inRange(range)) {
22542                     break;
22543                 }
22544                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22545                     break;
22546                 }
22547                 parent = parent.parentElement;
22548             }
22549             return parent;
22550         }
22551         
22552         // is ancestor a text element.
22553         var ac =  range.commonAncestorContainer;
22554         if (ac.nodeType == 3) {
22555             ac = ac.parentNode;
22556         }
22557         
22558         var ar = ac.childNodes;
22559          
22560         var nodes = [];
22561         var other_nodes = [];
22562         var has_other_nodes = false;
22563         for (var i=0;i<ar.length;i++) {
22564             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22565                 continue;
22566             }
22567             // fullly contained node.
22568             
22569             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22570                 nodes.push(ar[i]);
22571                 continue;
22572             }
22573             
22574             // probably selected..
22575             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22576                 other_nodes.push(ar[i]);
22577                 continue;
22578             }
22579             // outer..
22580             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22581                 continue;
22582             }
22583             
22584             
22585             has_other_nodes = true;
22586         }
22587         if (!nodes.length && other_nodes.length) {
22588             nodes= other_nodes;
22589         }
22590         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22591             return false;
22592         }
22593         
22594         return nodes[0];
22595     },
22596     createRange: function(sel)
22597     {
22598         // this has strange effects when using with 
22599         // top toolbar - not sure if it's a great idea.
22600         //this.editor.contentWindow.focus();
22601         if (typeof sel != "undefined") {
22602             try {
22603                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22604             } catch(e) {
22605                 return this.doc.createRange();
22606             }
22607         } else {
22608             return this.doc.createRange();
22609         }
22610     },
22611     getParentElement: function()
22612     {
22613         
22614         this.assignDocWin();
22615         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22616         
22617         var range = this.createRange(sel);
22618          
22619         try {
22620             var p = range.commonAncestorContainer;
22621             while (p.nodeType == 3) { // text node
22622                 p = p.parentNode;
22623             }
22624             return p;
22625         } catch (e) {
22626             return null;
22627         }
22628     
22629     },
22630     /***
22631      *
22632      * Range intersection.. the hard stuff...
22633      *  '-1' = before
22634      *  '0' = hits..
22635      *  '1' = after.
22636      *         [ -- selected range --- ]
22637      *   [fail]                        [fail]
22638      *
22639      *    basically..
22640      *      if end is before start or  hits it. fail.
22641      *      if start is after end or hits it fail.
22642      *
22643      *   if either hits (but other is outside. - then it's not 
22644      *   
22645      *    
22646      **/
22647     
22648     
22649     // @see http://www.thismuchiknow.co.uk/?p=64.
22650     rangeIntersectsNode : function(range, node)
22651     {
22652         var nodeRange = node.ownerDocument.createRange();
22653         try {
22654             nodeRange.selectNode(node);
22655         } catch (e) {
22656             nodeRange.selectNodeContents(node);
22657         }
22658     
22659         var rangeStartRange = range.cloneRange();
22660         rangeStartRange.collapse(true);
22661     
22662         var rangeEndRange = range.cloneRange();
22663         rangeEndRange.collapse(false);
22664     
22665         var nodeStartRange = nodeRange.cloneRange();
22666         nodeStartRange.collapse(true);
22667     
22668         var nodeEndRange = nodeRange.cloneRange();
22669         nodeEndRange.collapse(false);
22670     
22671         return rangeStartRange.compareBoundaryPoints(
22672                  Range.START_TO_START, nodeEndRange) == -1 &&
22673                rangeEndRange.compareBoundaryPoints(
22674                  Range.START_TO_START, nodeStartRange) == 1;
22675         
22676          
22677     },
22678     rangeCompareNode : function(range, node)
22679     {
22680         var nodeRange = node.ownerDocument.createRange();
22681         try {
22682             nodeRange.selectNode(node);
22683         } catch (e) {
22684             nodeRange.selectNodeContents(node);
22685         }
22686         
22687         
22688         range.collapse(true);
22689     
22690         nodeRange.collapse(true);
22691      
22692         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22693         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22694          
22695         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22696         
22697         var nodeIsBefore   =  ss == 1;
22698         var nodeIsAfter    = ee == -1;
22699         
22700         if (nodeIsBefore && nodeIsAfter) {
22701             return 0; // outer
22702         }
22703         if (!nodeIsBefore && nodeIsAfter) {
22704             return 1; //right trailed.
22705         }
22706         
22707         if (nodeIsBefore && !nodeIsAfter) {
22708             return 2;  // left trailed.
22709         }
22710         // fully contined.
22711         return 3;
22712     },
22713
22714     // private? - in a new class?
22715     cleanUpPaste :  function()
22716     {
22717         // cleans up the whole document..
22718         Roo.log('cleanuppaste');
22719         
22720         this.cleanUpChildren(this.doc.body);
22721         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22722         if (clean != this.doc.body.innerHTML) {
22723             this.doc.body.innerHTML = clean;
22724         }
22725         
22726     },
22727     
22728     cleanWordChars : function(input) {// change the chars to hex code
22729         var he = Roo.HtmlEditorCore;
22730         
22731         var output = input;
22732         Roo.each(he.swapCodes, function(sw) { 
22733             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22734             
22735             output = output.replace(swapper, sw[1]);
22736         });
22737         
22738         return output;
22739     },
22740     
22741     
22742     cleanUpChildren : function (n)
22743     {
22744         if (!n.childNodes.length) {
22745             return;
22746         }
22747         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22748            this.cleanUpChild(n.childNodes[i]);
22749         }
22750     },
22751     
22752     
22753         
22754     
22755     cleanUpChild : function (node)
22756     {
22757         var ed = this;
22758         //console.log(node);
22759         if (node.nodeName == "#text") {
22760             // clean up silly Windows -- stuff?
22761             return; 
22762         }
22763         if (node.nodeName == "#comment") {
22764             node.parentNode.removeChild(node);
22765             // clean up silly Windows -- stuff?
22766             return; 
22767         }
22768         var lcname = node.tagName.toLowerCase();
22769         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22770         // whitelist of tags..
22771         
22772         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22773             // remove node.
22774             node.parentNode.removeChild(node);
22775             return;
22776             
22777         }
22778         
22779         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22780         
22781         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22782         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22783         
22784         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22785         //    remove_keep_children = true;
22786         //}
22787         
22788         if (remove_keep_children) {
22789             this.cleanUpChildren(node);
22790             // inserts everything just before this node...
22791             while (node.childNodes.length) {
22792                 var cn = node.childNodes[0];
22793                 node.removeChild(cn);
22794                 node.parentNode.insertBefore(cn, node);
22795             }
22796             node.parentNode.removeChild(node);
22797             return;
22798         }
22799         
22800         if (!node.attributes || !node.attributes.length) {
22801             this.cleanUpChildren(node);
22802             return;
22803         }
22804         
22805         function cleanAttr(n,v)
22806         {
22807             
22808             if (v.match(/^\./) || v.match(/^\//)) {
22809                 return;
22810             }
22811             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22812                 return;
22813             }
22814             if (v.match(/^#/)) {
22815                 return;
22816             }
22817 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22818             node.removeAttribute(n);
22819             
22820         }
22821         
22822         var cwhite = this.cwhite;
22823         var cblack = this.cblack;
22824             
22825         function cleanStyle(n,v)
22826         {
22827             if (v.match(/expression/)) { //XSS?? should we even bother..
22828                 node.removeAttribute(n);
22829                 return;
22830             }
22831             
22832             var parts = v.split(/;/);
22833             var clean = [];
22834             
22835             Roo.each(parts, function(p) {
22836                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22837                 if (!p.length) {
22838                     return true;
22839                 }
22840                 var l = p.split(':').shift().replace(/\s+/g,'');
22841                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22842                 
22843                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22844 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22845                     //node.removeAttribute(n);
22846                     return true;
22847                 }
22848                 //Roo.log()
22849                 // only allow 'c whitelisted system attributes'
22850                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22851 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22852                     //node.removeAttribute(n);
22853                     return true;
22854                 }
22855                 
22856                 
22857                  
22858                 
22859                 clean.push(p);
22860                 return true;
22861             });
22862             if (clean.length) { 
22863                 node.setAttribute(n, clean.join(';'));
22864             } else {
22865                 node.removeAttribute(n);
22866             }
22867             
22868         }
22869         
22870         
22871         for (var i = node.attributes.length-1; i > -1 ; i--) {
22872             var a = node.attributes[i];
22873             //console.log(a);
22874             
22875             if (a.name.toLowerCase().substr(0,2)=='on')  {
22876                 node.removeAttribute(a.name);
22877                 continue;
22878             }
22879             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22880                 node.removeAttribute(a.name);
22881                 continue;
22882             }
22883             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22884                 cleanAttr(a.name,a.value); // fixme..
22885                 continue;
22886             }
22887             if (a.name == 'style') {
22888                 cleanStyle(a.name,a.value);
22889                 continue;
22890             }
22891             /// clean up MS crap..
22892             // tecnically this should be a list of valid class'es..
22893             
22894             
22895             if (a.name == 'class') {
22896                 if (a.value.match(/^Mso/)) {
22897                     node.className = '';
22898                 }
22899                 
22900                 if (a.value.match(/^body$/)) {
22901                     node.className = '';
22902                 }
22903                 continue;
22904             }
22905             
22906             // style cleanup!?
22907             // class cleanup?
22908             
22909         }
22910         
22911         
22912         this.cleanUpChildren(node);
22913         
22914         
22915     },
22916     
22917     /**
22918      * Clean up MS wordisms...
22919      */
22920     cleanWord : function(node)
22921     {
22922         
22923         
22924         if (!node) {
22925             this.cleanWord(this.doc.body);
22926             return;
22927         }
22928         if (node.nodeName == "#text") {
22929             // clean up silly Windows -- stuff?
22930             return; 
22931         }
22932         if (node.nodeName == "#comment") {
22933             node.parentNode.removeChild(node);
22934             // clean up silly Windows -- stuff?
22935             return; 
22936         }
22937         
22938         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22939             node.parentNode.removeChild(node);
22940             return;
22941         }
22942         
22943         // remove - but keep children..
22944         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22945             while (node.childNodes.length) {
22946                 var cn = node.childNodes[0];
22947                 node.removeChild(cn);
22948                 node.parentNode.insertBefore(cn, node);
22949             }
22950             node.parentNode.removeChild(node);
22951             this.iterateChildren(node, this.cleanWord);
22952             return;
22953         }
22954         // clean styles
22955         if (node.className.length) {
22956             
22957             var cn = node.className.split(/\W+/);
22958             var cna = [];
22959             Roo.each(cn, function(cls) {
22960                 if (cls.match(/Mso[a-zA-Z]+/)) {
22961                     return;
22962                 }
22963                 cna.push(cls);
22964             });
22965             node.className = cna.length ? cna.join(' ') : '';
22966             if (!cna.length) {
22967                 node.removeAttribute("class");
22968             }
22969         }
22970         
22971         if (node.hasAttribute("lang")) {
22972             node.removeAttribute("lang");
22973         }
22974         
22975         if (node.hasAttribute("style")) {
22976             
22977             var styles = node.getAttribute("style").split(";");
22978             var nstyle = [];
22979             Roo.each(styles, function(s) {
22980                 if (!s.match(/:/)) {
22981                     return;
22982                 }
22983                 var kv = s.split(":");
22984                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22985                     return;
22986                 }
22987                 // what ever is left... we allow.
22988                 nstyle.push(s);
22989             });
22990             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22991             if (!nstyle.length) {
22992                 node.removeAttribute('style');
22993             }
22994         }
22995         this.iterateChildren(node, this.cleanWord);
22996         
22997         
22998         
22999     },
23000     /**
23001      * iterateChildren of a Node, calling fn each time, using this as the scole..
23002      * @param {DomNode} node node to iterate children of.
23003      * @param {Function} fn method of this class to call on each item.
23004      */
23005     iterateChildren : function(node, fn)
23006     {
23007         if (!node.childNodes.length) {
23008                 return;
23009         }
23010         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23011            fn.call(this, node.childNodes[i])
23012         }
23013     },
23014     
23015     
23016     /**
23017      * cleanTableWidths.
23018      *
23019      * Quite often pasting from word etc.. results in tables with column and widths.
23020      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23021      *
23022      */
23023     cleanTableWidths : function(node)
23024     {
23025          
23026          
23027         if (!node) {
23028             this.cleanTableWidths(this.doc.body);
23029             return;
23030         }
23031         
23032         // ignore list...
23033         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23034             return; 
23035         }
23036         Roo.log(node.tagName);
23037         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23038             this.iterateChildren(node, this.cleanTableWidths);
23039             return;
23040         }
23041         if (node.hasAttribute('width')) {
23042             node.removeAttribute('width');
23043         }
23044         
23045          
23046         if (node.hasAttribute("style")) {
23047             // pretty basic...
23048             
23049             var styles = node.getAttribute("style").split(";");
23050             var nstyle = [];
23051             Roo.each(styles, function(s) {
23052                 if (!s.match(/:/)) {
23053                     return;
23054                 }
23055                 var kv = s.split(":");
23056                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23057                     return;
23058                 }
23059                 // what ever is left... we allow.
23060                 nstyle.push(s);
23061             });
23062             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23063             if (!nstyle.length) {
23064                 node.removeAttribute('style');
23065             }
23066         }
23067         
23068         this.iterateChildren(node, this.cleanTableWidths);
23069         
23070         
23071     },
23072     
23073     
23074     
23075     
23076     domToHTML : function(currentElement, depth, nopadtext) {
23077         
23078         depth = depth || 0;
23079         nopadtext = nopadtext || false;
23080     
23081         if (!currentElement) {
23082             return this.domToHTML(this.doc.body);
23083         }
23084         
23085         //Roo.log(currentElement);
23086         var j;
23087         var allText = false;
23088         var nodeName = currentElement.nodeName;
23089         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23090         
23091         if  (nodeName == '#text') {
23092             
23093             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23094         }
23095         
23096         
23097         var ret = '';
23098         if (nodeName != 'BODY') {
23099              
23100             var i = 0;
23101             // Prints the node tagName, such as <A>, <IMG>, etc
23102             if (tagName) {
23103                 var attr = [];
23104                 for(i = 0; i < currentElement.attributes.length;i++) {
23105                     // quoting?
23106                     var aname = currentElement.attributes.item(i).name;
23107                     if (!currentElement.attributes.item(i).value.length) {
23108                         continue;
23109                     }
23110                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23111                 }
23112                 
23113                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23114             } 
23115             else {
23116                 
23117                 // eack
23118             }
23119         } else {
23120             tagName = false;
23121         }
23122         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23123             return ret;
23124         }
23125         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23126             nopadtext = true;
23127         }
23128         
23129         
23130         // Traverse the tree
23131         i = 0;
23132         var currentElementChild = currentElement.childNodes.item(i);
23133         var allText = true;
23134         var innerHTML  = '';
23135         lastnode = '';
23136         while (currentElementChild) {
23137             // Formatting code (indent the tree so it looks nice on the screen)
23138             var nopad = nopadtext;
23139             if (lastnode == 'SPAN') {
23140                 nopad  = true;
23141             }
23142             // text
23143             if  (currentElementChild.nodeName == '#text') {
23144                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23145                 toadd = nopadtext ? toadd : toadd.trim();
23146                 if (!nopad && toadd.length > 80) {
23147                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23148                 }
23149                 innerHTML  += toadd;
23150                 
23151                 i++;
23152                 currentElementChild = currentElement.childNodes.item(i);
23153                 lastNode = '';
23154                 continue;
23155             }
23156             allText = false;
23157             
23158             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23159                 
23160             // Recursively traverse the tree structure of the child node
23161             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23162             lastnode = currentElementChild.nodeName;
23163             i++;
23164             currentElementChild=currentElement.childNodes.item(i);
23165         }
23166         
23167         ret += innerHTML;
23168         
23169         if (!allText) {
23170                 // The remaining code is mostly for formatting the tree
23171             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23172         }
23173         
23174         
23175         if (tagName) {
23176             ret+= "</"+tagName+">";
23177         }
23178         return ret;
23179         
23180     },
23181         
23182     applyBlacklists : function()
23183     {
23184         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23185         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23186         
23187         this.white = [];
23188         this.black = [];
23189         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23190             if (b.indexOf(tag) > -1) {
23191                 return;
23192             }
23193             this.white.push(tag);
23194             
23195         }, this);
23196         
23197         Roo.each(w, function(tag) {
23198             if (b.indexOf(tag) > -1) {
23199                 return;
23200             }
23201             if (this.white.indexOf(tag) > -1) {
23202                 return;
23203             }
23204             this.white.push(tag);
23205             
23206         }, this);
23207         
23208         
23209         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23210             if (w.indexOf(tag) > -1) {
23211                 return;
23212             }
23213             this.black.push(tag);
23214             
23215         }, this);
23216         
23217         Roo.each(b, function(tag) {
23218             if (w.indexOf(tag) > -1) {
23219                 return;
23220             }
23221             if (this.black.indexOf(tag) > -1) {
23222                 return;
23223             }
23224             this.black.push(tag);
23225             
23226         }, this);
23227         
23228         
23229         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23230         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23231         
23232         this.cwhite = [];
23233         this.cblack = [];
23234         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23235             if (b.indexOf(tag) > -1) {
23236                 return;
23237             }
23238             this.cwhite.push(tag);
23239             
23240         }, this);
23241         
23242         Roo.each(w, function(tag) {
23243             if (b.indexOf(tag) > -1) {
23244                 return;
23245             }
23246             if (this.cwhite.indexOf(tag) > -1) {
23247                 return;
23248             }
23249             this.cwhite.push(tag);
23250             
23251         }, this);
23252         
23253         
23254         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23255             if (w.indexOf(tag) > -1) {
23256                 return;
23257             }
23258             this.cblack.push(tag);
23259             
23260         }, this);
23261         
23262         Roo.each(b, function(tag) {
23263             if (w.indexOf(tag) > -1) {
23264                 return;
23265             }
23266             if (this.cblack.indexOf(tag) > -1) {
23267                 return;
23268             }
23269             this.cblack.push(tag);
23270             
23271         }, this);
23272     },
23273     
23274     setStylesheets : function(stylesheets)
23275     {
23276         if(typeof(stylesheets) == 'string'){
23277             Roo.get(this.iframe.contentDocument.head).createChild({
23278                 tag : 'link',
23279                 rel : 'stylesheet',
23280                 type : 'text/css',
23281                 href : stylesheets
23282             });
23283             
23284             return;
23285         }
23286         var _this = this;
23287      
23288         Roo.each(stylesheets, function(s) {
23289             if(!s.length){
23290                 return;
23291             }
23292             
23293             Roo.get(_this.iframe.contentDocument.head).createChild({
23294                 tag : 'link',
23295                 rel : 'stylesheet',
23296                 type : 'text/css',
23297                 href : s
23298             });
23299         });
23300
23301         
23302     },
23303     
23304     removeStylesheets : function()
23305     {
23306         var _this = this;
23307         
23308         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23309             s.remove();
23310         });
23311     },
23312     
23313     setStyle : function(style)
23314     {
23315         Roo.get(this.iframe.contentDocument.head).createChild({
23316             tag : 'style',
23317             type : 'text/css',
23318             html : style
23319         });
23320
23321         return;
23322     }
23323     
23324     // hide stuff that is not compatible
23325     /**
23326      * @event blur
23327      * @hide
23328      */
23329     /**
23330      * @event change
23331      * @hide
23332      */
23333     /**
23334      * @event focus
23335      * @hide
23336      */
23337     /**
23338      * @event specialkey
23339      * @hide
23340      */
23341     /**
23342      * @cfg {String} fieldClass @hide
23343      */
23344     /**
23345      * @cfg {String} focusClass @hide
23346      */
23347     /**
23348      * @cfg {String} autoCreate @hide
23349      */
23350     /**
23351      * @cfg {String} inputType @hide
23352      */
23353     /**
23354      * @cfg {String} invalidClass @hide
23355      */
23356     /**
23357      * @cfg {String} invalidText @hide
23358      */
23359     /**
23360      * @cfg {String} msgFx @hide
23361      */
23362     /**
23363      * @cfg {String} validateOnBlur @hide
23364      */
23365 });
23366
23367 Roo.HtmlEditorCore.white = [
23368         'area', 'br', 'img', 'input', 'hr', 'wbr',
23369         
23370        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23371        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23372        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23373        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23374        'table',   'ul',         'xmp', 
23375        
23376        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23377       'thead',   'tr', 
23378      
23379       'dir', 'menu', 'ol', 'ul', 'dl',
23380        
23381       'embed',  'object'
23382 ];
23383
23384
23385 Roo.HtmlEditorCore.black = [
23386     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23387         'applet', // 
23388         'base',   'basefont', 'bgsound', 'blink',  'body', 
23389         'frame',  'frameset', 'head',    'html',   'ilayer', 
23390         'iframe', 'layer',  'link',     'meta',    'object',   
23391         'script', 'style' ,'title',  'xml' // clean later..
23392 ];
23393 Roo.HtmlEditorCore.clean = [
23394     'script', 'style', 'title', 'xml'
23395 ];
23396 Roo.HtmlEditorCore.remove = [
23397     'font'
23398 ];
23399 // attributes..
23400
23401 Roo.HtmlEditorCore.ablack = [
23402     'on'
23403 ];
23404     
23405 Roo.HtmlEditorCore.aclean = [ 
23406     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23407 ];
23408
23409 // protocols..
23410 Roo.HtmlEditorCore.pwhite= [
23411         'http',  'https',  'mailto'
23412 ];
23413
23414 // white listed style attributes.
23415 Roo.HtmlEditorCore.cwhite= [
23416       //  'text-align', /// default is to allow most things..
23417       
23418          
23419 //        'font-size'//??
23420 ];
23421
23422 // black listed style attributes.
23423 Roo.HtmlEditorCore.cblack= [
23424       //  'font-size' -- this can be set by the project 
23425 ];
23426
23427
23428 Roo.HtmlEditorCore.swapCodes   =[ 
23429     [    8211, "--" ], 
23430     [    8212, "--" ], 
23431     [    8216,  "'" ],  
23432     [    8217, "'" ],  
23433     [    8220, '"' ],  
23434     [    8221, '"' ],  
23435     [    8226, "*" ],  
23436     [    8230, "..." ]
23437 ]; 
23438
23439     /*
23440  * - LGPL
23441  *
23442  * HtmlEditor
23443  * 
23444  */
23445
23446 /**
23447  * @class Roo.bootstrap.HtmlEditor
23448  * @extends Roo.bootstrap.TextArea
23449  * Bootstrap HtmlEditor class
23450
23451  * @constructor
23452  * Create a new HtmlEditor
23453  * @param {Object} config The config object
23454  */
23455
23456 Roo.bootstrap.HtmlEditor = function(config){
23457     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23458     if (!this.toolbars) {
23459         this.toolbars = [];
23460     }
23461     
23462     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23463     this.addEvents({
23464             /**
23465              * @event initialize
23466              * Fires when the editor is fully initialized (including the iframe)
23467              * @param {HtmlEditor} this
23468              */
23469             initialize: true,
23470             /**
23471              * @event activate
23472              * Fires when the editor is first receives the focus. Any insertion must wait
23473              * until after this event.
23474              * @param {HtmlEditor} this
23475              */
23476             activate: true,
23477              /**
23478              * @event beforesync
23479              * Fires before the textarea is updated with content from the editor iframe. Return false
23480              * to cancel the sync.
23481              * @param {HtmlEditor} this
23482              * @param {String} html
23483              */
23484             beforesync: true,
23485              /**
23486              * @event beforepush
23487              * Fires before the iframe editor is updated with content from the textarea. Return false
23488              * to cancel the push.
23489              * @param {HtmlEditor} this
23490              * @param {String} html
23491              */
23492             beforepush: true,
23493              /**
23494              * @event sync
23495              * Fires when the textarea is updated with content from the editor iframe.
23496              * @param {HtmlEditor} this
23497              * @param {String} html
23498              */
23499             sync: true,
23500              /**
23501              * @event push
23502              * Fires when the iframe editor is updated with content from the textarea.
23503              * @param {HtmlEditor} this
23504              * @param {String} html
23505              */
23506             push: true,
23507              /**
23508              * @event editmodechange
23509              * Fires when the editor switches edit modes
23510              * @param {HtmlEditor} this
23511              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23512              */
23513             editmodechange: true,
23514             /**
23515              * @event editorevent
23516              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23517              * @param {HtmlEditor} this
23518              */
23519             editorevent: true,
23520             /**
23521              * @event firstfocus
23522              * Fires when on first focus - needed by toolbars..
23523              * @param {HtmlEditor} this
23524              */
23525             firstfocus: true,
23526             /**
23527              * @event autosave
23528              * Auto save the htmlEditor value as a file into Events
23529              * @param {HtmlEditor} this
23530              */
23531             autosave: true,
23532             /**
23533              * @event savedpreview
23534              * preview the saved version of htmlEditor
23535              * @param {HtmlEditor} this
23536              */
23537             savedpreview: true
23538         });
23539 };
23540
23541
23542 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23543     
23544     
23545       /**
23546      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23547      */
23548     toolbars : false,
23549     
23550      /**
23551     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23552     */
23553     btns : [],
23554    
23555      /**
23556      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23557      *                        Roo.resizable.
23558      */
23559     resizable : false,
23560      /**
23561      * @cfg {Number} height (in pixels)
23562      */   
23563     height: 300,
23564    /**
23565      * @cfg {Number} width (in pixels)
23566      */   
23567     width: false,
23568     
23569     /**
23570      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23571      * 
23572      */
23573     stylesheets: false,
23574     
23575     // id of frame..
23576     frameId: false,
23577     
23578     // private properties
23579     validationEvent : false,
23580     deferHeight: true,
23581     initialized : false,
23582     activated : false,
23583     
23584     onFocus : Roo.emptyFn,
23585     iframePad:3,
23586     hideMode:'offsets',
23587     
23588     tbContainer : false,
23589     
23590     bodyCls : '',
23591     
23592     toolbarContainer :function() {
23593         return this.wrap.select('.x-html-editor-tb',true).first();
23594     },
23595
23596     /**
23597      * Protected method that will not generally be called directly. It
23598      * is called when the editor creates its toolbar. Override this method if you need to
23599      * add custom toolbar buttons.
23600      * @param {HtmlEditor} editor
23601      */
23602     createToolbar : function(){
23603         Roo.log('renewing');
23604         Roo.log("create toolbars");
23605         
23606         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23607         this.toolbars[0].render(this.toolbarContainer());
23608         
23609         return;
23610         
23611 //        if (!editor.toolbars || !editor.toolbars.length) {
23612 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23613 //        }
23614 //        
23615 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23616 //            editor.toolbars[i] = Roo.factory(
23617 //                    typeof(editor.toolbars[i]) == 'string' ?
23618 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23619 //                Roo.bootstrap.HtmlEditor);
23620 //            editor.toolbars[i].init(editor);
23621 //        }
23622     },
23623
23624      
23625     // private
23626     onRender : function(ct, position)
23627     {
23628        // Roo.log("Call onRender: " + this.xtype);
23629         var _t = this;
23630         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23631       
23632         this.wrap = this.inputEl().wrap({
23633             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23634         });
23635         
23636         this.editorcore.onRender(ct, position);
23637          
23638         if (this.resizable) {
23639             this.resizeEl = new Roo.Resizable(this.wrap, {
23640                 pinned : true,
23641                 wrap: true,
23642                 dynamic : true,
23643                 minHeight : this.height,
23644                 height: this.height,
23645                 handles : this.resizable,
23646                 width: this.width,
23647                 listeners : {
23648                     resize : function(r, w, h) {
23649                         _t.onResize(w,h); // -something
23650                     }
23651                 }
23652             });
23653             
23654         }
23655         this.createToolbar(this);
23656        
23657         
23658         if(!this.width && this.resizable){
23659             this.setSize(this.wrap.getSize());
23660         }
23661         if (this.resizeEl) {
23662             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23663             // should trigger onReize..
23664         }
23665         
23666     },
23667
23668     // private
23669     onResize : function(w, h)
23670     {
23671         Roo.log('resize: ' +w + ',' + h );
23672         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23673         var ew = false;
23674         var eh = false;
23675         
23676         if(this.inputEl() ){
23677             if(typeof w == 'number'){
23678                 var aw = w - this.wrap.getFrameWidth('lr');
23679                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23680                 ew = aw;
23681             }
23682             if(typeof h == 'number'){
23683                  var tbh = -11;  // fixme it needs to tool bar size!
23684                 for (var i =0; i < this.toolbars.length;i++) {
23685                     // fixme - ask toolbars for heights?
23686                     tbh += this.toolbars[i].el.getHeight();
23687                     //if (this.toolbars[i].footer) {
23688                     //    tbh += this.toolbars[i].footer.el.getHeight();
23689                     //}
23690                 }
23691               
23692                 
23693                 
23694                 
23695                 
23696                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23697                 ah -= 5; // knock a few pixes off for look..
23698                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23699                 var eh = ah;
23700             }
23701         }
23702         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23703         this.editorcore.onResize(ew,eh);
23704         
23705     },
23706
23707     /**
23708      * Toggles the editor between standard and source edit mode.
23709      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23710      */
23711     toggleSourceEdit : function(sourceEditMode)
23712     {
23713         this.editorcore.toggleSourceEdit(sourceEditMode);
23714         
23715         if(this.editorcore.sourceEditMode){
23716             Roo.log('editor - showing textarea');
23717             
23718 //            Roo.log('in');
23719 //            Roo.log(this.syncValue());
23720             this.syncValue();
23721             this.inputEl().removeClass(['hide', 'x-hidden']);
23722             this.inputEl().dom.removeAttribute('tabIndex');
23723             this.inputEl().focus();
23724         }else{
23725             Roo.log('editor - hiding textarea');
23726 //            Roo.log('out')
23727 //            Roo.log(this.pushValue()); 
23728             this.pushValue();
23729             
23730             this.inputEl().addClass(['hide', 'x-hidden']);
23731             this.inputEl().dom.setAttribute('tabIndex', -1);
23732             //this.deferFocus();
23733         }
23734          
23735         if(this.resizable){
23736             this.setSize(this.wrap.getSize());
23737         }
23738         
23739         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23740     },
23741  
23742     // private (for BoxComponent)
23743     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23744
23745     // private (for BoxComponent)
23746     getResizeEl : function(){
23747         return this.wrap;
23748     },
23749
23750     // private (for BoxComponent)
23751     getPositionEl : function(){
23752         return this.wrap;
23753     },
23754
23755     // private
23756     initEvents : function(){
23757         this.originalValue = this.getValue();
23758     },
23759
23760 //    /**
23761 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23762 //     * @method
23763 //     */
23764 //    markInvalid : Roo.emptyFn,
23765 //    /**
23766 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23767 //     * @method
23768 //     */
23769 //    clearInvalid : Roo.emptyFn,
23770
23771     setValue : function(v){
23772         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23773         this.editorcore.pushValue();
23774     },
23775
23776      
23777     // private
23778     deferFocus : function(){
23779         this.focus.defer(10, this);
23780     },
23781
23782     // doc'ed in Field
23783     focus : function(){
23784         this.editorcore.focus();
23785         
23786     },
23787       
23788
23789     // private
23790     onDestroy : function(){
23791         
23792         
23793         
23794         if(this.rendered){
23795             
23796             for (var i =0; i < this.toolbars.length;i++) {
23797                 // fixme - ask toolbars for heights?
23798                 this.toolbars[i].onDestroy();
23799             }
23800             
23801             this.wrap.dom.innerHTML = '';
23802             this.wrap.remove();
23803         }
23804     },
23805
23806     // private
23807     onFirstFocus : function(){
23808         //Roo.log("onFirstFocus");
23809         this.editorcore.onFirstFocus();
23810          for (var i =0; i < this.toolbars.length;i++) {
23811             this.toolbars[i].onFirstFocus();
23812         }
23813         
23814     },
23815     
23816     // private
23817     syncValue : function()
23818     {   
23819         this.editorcore.syncValue();
23820     },
23821     
23822     pushValue : function()
23823     {   
23824         this.editorcore.pushValue();
23825     }
23826      
23827     
23828     // hide stuff that is not compatible
23829     /**
23830      * @event blur
23831      * @hide
23832      */
23833     /**
23834      * @event change
23835      * @hide
23836      */
23837     /**
23838      * @event focus
23839      * @hide
23840      */
23841     /**
23842      * @event specialkey
23843      * @hide
23844      */
23845     /**
23846      * @cfg {String} fieldClass @hide
23847      */
23848     /**
23849      * @cfg {String} focusClass @hide
23850      */
23851     /**
23852      * @cfg {String} autoCreate @hide
23853      */
23854     /**
23855      * @cfg {String} inputType @hide
23856      */
23857     /**
23858      * @cfg {String} invalidClass @hide
23859      */
23860     /**
23861      * @cfg {String} invalidText @hide
23862      */
23863     /**
23864      * @cfg {String} msgFx @hide
23865      */
23866     /**
23867      * @cfg {String} validateOnBlur @hide
23868      */
23869 });
23870  
23871     
23872    
23873    
23874    
23875       
23876 Roo.namespace('Roo.bootstrap.htmleditor');
23877 /**
23878  * @class Roo.bootstrap.HtmlEditorToolbar1
23879  * Basic Toolbar
23880  * 
23881  * Usage:
23882  *
23883  new Roo.bootstrap.HtmlEditor({
23884     ....
23885     toolbars : [
23886         new Roo.bootstrap.HtmlEditorToolbar1({
23887             disable : { fonts: 1 , format: 1, ..., ... , ...],
23888             btns : [ .... ]
23889         })
23890     }
23891      
23892  * 
23893  * @cfg {Object} disable List of elements to disable..
23894  * @cfg {Array} btns List of additional buttons.
23895  * 
23896  * 
23897  * NEEDS Extra CSS? 
23898  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23899  */
23900  
23901 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23902 {
23903     
23904     Roo.apply(this, config);
23905     
23906     // default disabled, based on 'good practice'..
23907     this.disable = this.disable || {};
23908     Roo.applyIf(this.disable, {
23909         fontSize : true,
23910         colors : true,
23911         specialElements : true
23912     });
23913     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23914     
23915     this.editor = config.editor;
23916     this.editorcore = config.editor.editorcore;
23917     
23918     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23919     
23920     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23921     // dont call parent... till later.
23922 }
23923 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23924      
23925     bar : true,
23926     
23927     editor : false,
23928     editorcore : false,
23929     
23930     
23931     formats : [
23932         "p" ,  
23933         "h1","h2","h3","h4","h5","h6", 
23934         "pre", "code", 
23935         "abbr", "acronym", "address", "cite", "samp", "var",
23936         'div','span'
23937     ],
23938     
23939     onRender : function(ct, position)
23940     {
23941        // Roo.log("Call onRender: " + this.xtype);
23942         
23943        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23944        Roo.log(this.el);
23945        this.el.dom.style.marginBottom = '0';
23946        var _this = this;
23947        var editorcore = this.editorcore;
23948        var editor= this.editor;
23949        
23950        var children = [];
23951        var btn = function(id,cmd , toggle, handler, html){
23952        
23953             var  event = toggle ? 'toggle' : 'click';
23954        
23955             var a = {
23956                 size : 'sm',
23957                 xtype: 'Button',
23958                 xns: Roo.bootstrap,
23959                 glyphicon : id,
23960                 cmd : id || cmd,
23961                 enableToggle:toggle !== false,
23962                 html : html || '',
23963                 pressed : toggle ? false : null,
23964                 listeners : {}
23965             };
23966             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23967                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23968             };
23969             children.push(a);
23970             return a;
23971        }
23972        
23973     //    var cb_box = function...
23974         
23975         var style = {
23976                 xtype: 'Button',
23977                 size : 'sm',
23978                 xns: Roo.bootstrap,
23979                 glyphicon : 'font',
23980                 //html : 'submit'
23981                 menu : {
23982                     xtype: 'Menu',
23983                     xns: Roo.bootstrap,
23984                     items:  []
23985                 }
23986         };
23987         Roo.each(this.formats, function(f) {
23988             style.menu.items.push({
23989                 xtype :'MenuItem',
23990                 xns: Roo.bootstrap,
23991                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23992                 tagname : f,
23993                 listeners : {
23994                     click : function()
23995                     {
23996                         editorcore.insertTag(this.tagname);
23997                         editor.focus();
23998                     }
23999                 }
24000                 
24001             });
24002         });
24003         children.push(style);   
24004         
24005         btn('bold',false,true);
24006         btn('italic',false,true);
24007         btn('align-left', 'justifyleft',true);
24008         btn('align-center', 'justifycenter',true);
24009         btn('align-right' , 'justifyright',true);
24010         btn('link', false, false, function(btn) {
24011             //Roo.log("create link?");
24012             var url = prompt(this.createLinkText, this.defaultLinkValue);
24013             if(url && url != 'http:/'+'/'){
24014                 this.editorcore.relayCmd('createlink', url);
24015             }
24016         }),
24017         btn('list','insertunorderedlist',true);
24018         btn('pencil', false,true, function(btn){
24019                 Roo.log(this);
24020                 this.toggleSourceEdit(btn.pressed);
24021         });
24022         
24023         if (this.editor.btns.length > 0) {
24024             for (var i = 0; i<this.editor.btns.length; i++) {
24025                 children.push(this.editor.btns[i]);
24026             }
24027         }
24028         
24029         /*
24030         var cog = {
24031                 xtype: 'Button',
24032                 size : 'sm',
24033                 xns: Roo.bootstrap,
24034                 glyphicon : 'cog',
24035                 //html : 'submit'
24036                 menu : {
24037                     xtype: 'Menu',
24038                     xns: Roo.bootstrap,
24039                     items:  []
24040                 }
24041         };
24042         
24043         cog.menu.items.push({
24044             xtype :'MenuItem',
24045             xns: Roo.bootstrap,
24046             html : Clean styles,
24047             tagname : f,
24048             listeners : {
24049                 click : function()
24050                 {
24051                     editorcore.insertTag(this.tagname);
24052                     editor.focus();
24053                 }
24054             }
24055             
24056         });
24057        */
24058         
24059          
24060        this.xtype = 'NavSimplebar';
24061         
24062         for(var i=0;i< children.length;i++) {
24063             
24064             this.buttons.add(this.addxtypeChild(children[i]));
24065             
24066         }
24067         
24068         editor.on('editorevent', this.updateToolbar, this);
24069     },
24070     onBtnClick : function(id)
24071     {
24072        this.editorcore.relayCmd(id);
24073        this.editorcore.focus();
24074     },
24075     
24076     /**
24077      * Protected method that will not generally be called directly. It triggers
24078      * a toolbar update by reading the markup state of the current selection in the editor.
24079      */
24080     updateToolbar: function(){
24081
24082         if(!this.editorcore.activated){
24083             this.editor.onFirstFocus(); // is this neeed?
24084             return;
24085         }
24086
24087         var btns = this.buttons; 
24088         var doc = this.editorcore.doc;
24089         btns.get('bold').setActive(doc.queryCommandState('bold'));
24090         btns.get('italic').setActive(doc.queryCommandState('italic'));
24091         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24092         
24093         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24094         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24095         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24096         
24097         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24098         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24099          /*
24100         
24101         var ans = this.editorcore.getAllAncestors();
24102         if (this.formatCombo) {
24103             
24104             
24105             var store = this.formatCombo.store;
24106             this.formatCombo.setValue("");
24107             for (var i =0; i < ans.length;i++) {
24108                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24109                     // select it..
24110                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24111                     break;
24112                 }
24113             }
24114         }
24115         
24116         
24117         
24118         // hides menus... - so this cant be on a menu...
24119         Roo.bootstrap.MenuMgr.hideAll();
24120         */
24121         Roo.bootstrap.MenuMgr.hideAll();
24122         //this.editorsyncValue();
24123     },
24124     onFirstFocus: function() {
24125         this.buttons.each(function(item){
24126            item.enable();
24127         });
24128     },
24129     toggleSourceEdit : function(sourceEditMode){
24130         
24131           
24132         if(sourceEditMode){
24133             Roo.log("disabling buttons");
24134            this.buttons.each( function(item){
24135                 if(item.cmd != 'pencil'){
24136                     item.disable();
24137                 }
24138             });
24139           
24140         }else{
24141             Roo.log("enabling buttons");
24142             if(this.editorcore.initialized){
24143                 this.buttons.each( function(item){
24144                     item.enable();
24145                 });
24146             }
24147             
24148         }
24149         Roo.log("calling toggole on editor");
24150         // tell the editor that it's been pressed..
24151         this.editor.toggleSourceEdit(sourceEditMode);
24152        
24153     }
24154 });
24155
24156
24157
24158
24159
24160 /**
24161  * @class Roo.bootstrap.Table.AbstractSelectionModel
24162  * @extends Roo.util.Observable
24163  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24164  * implemented by descendant classes.  This class should not be directly instantiated.
24165  * @constructor
24166  */
24167 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24168     this.locked = false;
24169     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24170 };
24171
24172
24173 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24174     /** @ignore Called by the grid automatically. Do not call directly. */
24175     init : function(grid){
24176         this.grid = grid;
24177         this.initEvents();
24178     },
24179
24180     /**
24181      * Locks the selections.
24182      */
24183     lock : function(){
24184         this.locked = true;
24185     },
24186
24187     /**
24188      * Unlocks the selections.
24189      */
24190     unlock : function(){
24191         this.locked = false;
24192     },
24193
24194     /**
24195      * Returns true if the selections are locked.
24196      * @return {Boolean}
24197      */
24198     isLocked : function(){
24199         return this.locked;
24200     }
24201 });
24202 /**
24203  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24204  * @class Roo.bootstrap.Table.RowSelectionModel
24205  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24206  * It supports multiple selections and keyboard selection/navigation. 
24207  * @constructor
24208  * @param {Object} config
24209  */
24210
24211 Roo.bootstrap.Table.RowSelectionModel = function(config){
24212     Roo.apply(this, config);
24213     this.selections = new Roo.util.MixedCollection(false, function(o){
24214         return o.id;
24215     });
24216
24217     this.last = false;
24218     this.lastActive = false;
24219
24220     this.addEvents({
24221         /**
24222              * @event selectionchange
24223              * Fires when the selection changes
24224              * @param {SelectionModel} this
24225              */
24226             "selectionchange" : true,
24227         /**
24228              * @event afterselectionchange
24229              * Fires after the selection changes (eg. by key press or clicking)
24230              * @param {SelectionModel} this
24231              */
24232             "afterselectionchange" : true,
24233         /**
24234              * @event beforerowselect
24235              * Fires when a row is selected being selected, return false to cancel.
24236              * @param {SelectionModel} this
24237              * @param {Number} rowIndex The selected index
24238              * @param {Boolean} keepExisting False if other selections will be cleared
24239              */
24240             "beforerowselect" : true,
24241         /**
24242              * @event rowselect
24243              * Fires when a row is selected.
24244              * @param {SelectionModel} this
24245              * @param {Number} rowIndex The selected index
24246              * @param {Roo.data.Record} r The record
24247              */
24248             "rowselect" : true,
24249         /**
24250              * @event rowdeselect
24251              * Fires when a row is deselected.
24252              * @param {SelectionModel} this
24253              * @param {Number} rowIndex The selected index
24254              */
24255         "rowdeselect" : true
24256     });
24257     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24258     this.locked = false;
24259  };
24260
24261 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24262     /**
24263      * @cfg {Boolean} singleSelect
24264      * True to allow selection of only one row at a time (defaults to false)
24265      */
24266     singleSelect : false,
24267
24268     // private
24269     initEvents : function()
24270     {
24271
24272         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24273         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24274         //}else{ // allow click to work like normal
24275          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24276         //}
24277         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24278         this.grid.on("rowclick", this.handleMouseDown, this);
24279         
24280         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24281             "up" : function(e){
24282                 if(!e.shiftKey){
24283                     this.selectPrevious(e.shiftKey);
24284                 }else if(this.last !== false && this.lastActive !== false){
24285                     var last = this.last;
24286                     this.selectRange(this.last,  this.lastActive-1);
24287                     this.grid.getView().focusRow(this.lastActive);
24288                     if(last !== false){
24289                         this.last = last;
24290                     }
24291                 }else{
24292                     this.selectFirstRow();
24293                 }
24294                 this.fireEvent("afterselectionchange", this);
24295             },
24296             "down" : function(e){
24297                 if(!e.shiftKey){
24298                     this.selectNext(e.shiftKey);
24299                 }else if(this.last !== false && this.lastActive !== false){
24300                     var last = this.last;
24301                     this.selectRange(this.last,  this.lastActive+1);
24302                     this.grid.getView().focusRow(this.lastActive);
24303                     if(last !== false){
24304                         this.last = last;
24305                     }
24306                 }else{
24307                     this.selectFirstRow();
24308                 }
24309                 this.fireEvent("afterselectionchange", this);
24310             },
24311             scope: this
24312         });
24313         this.grid.store.on('load', function(){
24314             this.selections.clear();
24315         },this);
24316         /*
24317         var view = this.grid.view;
24318         view.on("refresh", this.onRefresh, this);
24319         view.on("rowupdated", this.onRowUpdated, this);
24320         view.on("rowremoved", this.onRemove, this);
24321         */
24322     },
24323
24324     // private
24325     onRefresh : function()
24326     {
24327         var ds = this.grid.store, i, v = this.grid.view;
24328         var s = this.selections;
24329         s.each(function(r){
24330             if((i = ds.indexOfId(r.id)) != -1){
24331                 v.onRowSelect(i);
24332             }else{
24333                 s.remove(r);
24334             }
24335         });
24336     },
24337
24338     // private
24339     onRemove : function(v, index, r){
24340         this.selections.remove(r);
24341     },
24342
24343     // private
24344     onRowUpdated : function(v, index, r){
24345         if(this.isSelected(r)){
24346             v.onRowSelect(index);
24347         }
24348     },
24349
24350     /**
24351      * Select records.
24352      * @param {Array} records The records to select
24353      * @param {Boolean} keepExisting (optional) True to keep existing selections
24354      */
24355     selectRecords : function(records, keepExisting)
24356     {
24357         if(!keepExisting){
24358             this.clearSelections();
24359         }
24360             var ds = this.grid.store;
24361         for(var i = 0, len = records.length; i < len; i++){
24362             this.selectRow(ds.indexOf(records[i]), true);
24363         }
24364     },
24365
24366     /**
24367      * Gets the number of selected rows.
24368      * @return {Number}
24369      */
24370     getCount : function(){
24371         return this.selections.length;
24372     },
24373
24374     /**
24375      * Selects the first row in the grid.
24376      */
24377     selectFirstRow : function(){
24378         this.selectRow(0);
24379     },
24380
24381     /**
24382      * Select the last row.
24383      * @param {Boolean} keepExisting (optional) True to keep existing selections
24384      */
24385     selectLastRow : function(keepExisting){
24386         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24387         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24388     },
24389
24390     /**
24391      * Selects the row immediately following the last selected row.
24392      * @param {Boolean} keepExisting (optional) True to keep existing selections
24393      */
24394     selectNext : function(keepExisting)
24395     {
24396             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24397             this.selectRow(this.last+1, keepExisting);
24398             this.grid.getView().focusRow(this.last);
24399         }
24400     },
24401
24402     /**
24403      * Selects the row that precedes the last selected row.
24404      * @param {Boolean} keepExisting (optional) True to keep existing selections
24405      */
24406     selectPrevious : function(keepExisting){
24407         if(this.last){
24408             this.selectRow(this.last-1, keepExisting);
24409             this.grid.getView().focusRow(this.last);
24410         }
24411     },
24412
24413     /**
24414      * Returns the selected records
24415      * @return {Array} Array of selected records
24416      */
24417     getSelections : function(){
24418         return [].concat(this.selections.items);
24419     },
24420
24421     /**
24422      * Returns the first selected record.
24423      * @return {Record}
24424      */
24425     getSelected : function(){
24426         return this.selections.itemAt(0);
24427     },
24428
24429
24430     /**
24431      * Clears all selections.
24432      */
24433     clearSelections : function(fast)
24434     {
24435         if(this.locked) {
24436             return;
24437         }
24438         if(fast !== true){
24439                 var ds = this.grid.store;
24440             var s = this.selections;
24441             s.each(function(r){
24442                 this.deselectRow(ds.indexOfId(r.id));
24443             }, this);
24444             s.clear();
24445         }else{
24446             this.selections.clear();
24447         }
24448         this.last = false;
24449     },
24450
24451
24452     /**
24453      * Selects all rows.
24454      */
24455     selectAll : function(){
24456         if(this.locked) {
24457             return;
24458         }
24459         this.selections.clear();
24460         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24461             this.selectRow(i, true);
24462         }
24463     },
24464
24465     /**
24466      * Returns True if there is a selection.
24467      * @return {Boolean}
24468      */
24469     hasSelection : function(){
24470         return this.selections.length > 0;
24471     },
24472
24473     /**
24474      * Returns True if the specified row is selected.
24475      * @param {Number/Record} record The record or index of the record to check
24476      * @return {Boolean}
24477      */
24478     isSelected : function(index){
24479             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24480         return (r && this.selections.key(r.id) ? true : false);
24481     },
24482
24483     /**
24484      * Returns True if the specified record id is selected.
24485      * @param {String} id The id of record to check
24486      * @return {Boolean}
24487      */
24488     isIdSelected : function(id){
24489         return (this.selections.key(id) ? true : false);
24490     },
24491
24492
24493     // private
24494     handleMouseDBClick : function(e, t){
24495         
24496     },
24497     // private
24498     handleMouseDown : function(e, t)
24499     {
24500             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24501         if(this.isLocked() || rowIndex < 0 ){
24502             return;
24503         };
24504         if(e.shiftKey && this.last !== false){
24505             var last = this.last;
24506             this.selectRange(last, rowIndex, e.ctrlKey);
24507             this.last = last; // reset the last
24508             t.focus();
24509     
24510         }else{
24511             var isSelected = this.isSelected(rowIndex);
24512             //Roo.log("select row:" + rowIndex);
24513             if(isSelected){
24514                 this.deselectRow(rowIndex);
24515             } else {
24516                         this.selectRow(rowIndex, true);
24517             }
24518     
24519             /*
24520                 if(e.button !== 0 && isSelected){
24521                 alert('rowIndex 2: ' + rowIndex);
24522                     view.focusRow(rowIndex);
24523                 }else if(e.ctrlKey && isSelected){
24524                     this.deselectRow(rowIndex);
24525                 }else if(!isSelected){
24526                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24527                     view.focusRow(rowIndex);
24528                 }
24529             */
24530         }
24531         this.fireEvent("afterselectionchange", this);
24532     },
24533     // private
24534     handleDragableRowClick :  function(grid, rowIndex, e) 
24535     {
24536         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24537             this.selectRow(rowIndex, false);
24538             grid.view.focusRow(rowIndex);
24539              this.fireEvent("afterselectionchange", this);
24540         }
24541     },
24542     
24543     /**
24544      * Selects multiple rows.
24545      * @param {Array} rows Array of the indexes of the row to select
24546      * @param {Boolean} keepExisting (optional) True to keep existing selections
24547      */
24548     selectRows : function(rows, keepExisting){
24549         if(!keepExisting){
24550             this.clearSelections();
24551         }
24552         for(var i = 0, len = rows.length; i < len; i++){
24553             this.selectRow(rows[i], true);
24554         }
24555     },
24556
24557     /**
24558      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24559      * @param {Number} startRow The index of the first row in the range
24560      * @param {Number} endRow The index of the last row in the range
24561      * @param {Boolean} keepExisting (optional) True to retain existing selections
24562      */
24563     selectRange : function(startRow, endRow, keepExisting){
24564         if(this.locked) {
24565             return;
24566         }
24567         if(!keepExisting){
24568             this.clearSelections();
24569         }
24570         if(startRow <= endRow){
24571             for(var i = startRow; i <= endRow; i++){
24572                 this.selectRow(i, true);
24573             }
24574         }else{
24575             for(var i = startRow; i >= endRow; i--){
24576                 this.selectRow(i, true);
24577             }
24578         }
24579     },
24580
24581     /**
24582      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24583      * @param {Number} startRow The index of the first row in the range
24584      * @param {Number} endRow The index of the last row in the range
24585      */
24586     deselectRange : function(startRow, endRow, preventViewNotify){
24587         if(this.locked) {
24588             return;
24589         }
24590         for(var i = startRow; i <= endRow; i++){
24591             this.deselectRow(i, preventViewNotify);
24592         }
24593     },
24594
24595     /**
24596      * Selects a row.
24597      * @param {Number} row The index of the row to select
24598      * @param {Boolean} keepExisting (optional) True to keep existing selections
24599      */
24600     selectRow : function(index, keepExisting, preventViewNotify)
24601     {
24602             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24603             return;
24604         }
24605         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24606             if(!keepExisting || this.singleSelect){
24607                 this.clearSelections();
24608             }
24609             
24610             var r = this.grid.store.getAt(index);
24611             //console.log('selectRow - record id :' + r.id);
24612             
24613             this.selections.add(r);
24614             this.last = this.lastActive = index;
24615             if(!preventViewNotify){
24616                 var proxy = new Roo.Element(
24617                                 this.grid.getRowDom(index)
24618                 );
24619                 proxy.addClass('bg-info info');
24620             }
24621             this.fireEvent("rowselect", this, index, r);
24622             this.fireEvent("selectionchange", this);
24623         }
24624     },
24625
24626     /**
24627      * Deselects a row.
24628      * @param {Number} row The index of the row to deselect
24629      */
24630     deselectRow : function(index, preventViewNotify)
24631     {
24632         if(this.locked) {
24633             return;
24634         }
24635         if(this.last == index){
24636             this.last = false;
24637         }
24638         if(this.lastActive == index){
24639             this.lastActive = false;
24640         }
24641         
24642         var r = this.grid.store.getAt(index);
24643         if (!r) {
24644             return;
24645         }
24646         
24647         this.selections.remove(r);
24648         //.console.log('deselectRow - record id :' + r.id);
24649         if(!preventViewNotify){
24650         
24651             var proxy = new Roo.Element(
24652                 this.grid.getRowDom(index)
24653             );
24654             proxy.removeClass('bg-info info');
24655         }
24656         this.fireEvent("rowdeselect", this, index);
24657         this.fireEvent("selectionchange", this);
24658     },
24659
24660     // private
24661     restoreLast : function(){
24662         if(this._last){
24663             this.last = this._last;
24664         }
24665     },
24666
24667     // private
24668     acceptsNav : function(row, col, cm){
24669         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24670     },
24671
24672     // private
24673     onEditorKey : function(field, e){
24674         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24675         if(k == e.TAB){
24676             e.stopEvent();
24677             ed.completeEdit();
24678             if(e.shiftKey){
24679                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24680             }else{
24681                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24682             }
24683         }else if(k == e.ENTER && !e.ctrlKey){
24684             e.stopEvent();
24685             ed.completeEdit();
24686             if(e.shiftKey){
24687                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24688             }else{
24689                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24690             }
24691         }else if(k == e.ESC){
24692             ed.cancelEdit();
24693         }
24694         if(newCell){
24695             g.startEditing(newCell[0], newCell[1]);
24696         }
24697     }
24698 });
24699 /*
24700  * Based on:
24701  * Ext JS Library 1.1.1
24702  * Copyright(c) 2006-2007, Ext JS, LLC.
24703  *
24704  * Originally Released Under LGPL - original licence link has changed is not relivant.
24705  *
24706  * Fork - LGPL
24707  * <script type="text/javascript">
24708  */
24709  
24710 /**
24711  * @class Roo.bootstrap.PagingToolbar
24712  * @extends Roo.bootstrap.NavSimplebar
24713  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24714  * @constructor
24715  * Create a new PagingToolbar
24716  * @param {Object} config The config object
24717  * @param {Roo.data.Store} store
24718  */
24719 Roo.bootstrap.PagingToolbar = function(config)
24720 {
24721     // old args format still supported... - xtype is prefered..
24722         // created from xtype...
24723     
24724     this.ds = config.dataSource;
24725     
24726     if (config.store && !this.ds) {
24727         this.store= Roo.factory(config.store, Roo.data);
24728         this.ds = this.store;
24729         this.ds.xmodule = this.xmodule || false;
24730     }
24731     
24732     this.toolbarItems = [];
24733     if (config.items) {
24734         this.toolbarItems = config.items;
24735     }
24736     
24737     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24738     
24739     this.cursor = 0;
24740     
24741     if (this.ds) { 
24742         this.bind(this.ds);
24743     }
24744     
24745     if (Roo.bootstrap.version == 4) {
24746         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24747     } else {
24748         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24749     }
24750     
24751 };
24752
24753 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24754     /**
24755      * @cfg {Roo.data.Store} dataSource
24756      * The underlying data store providing the paged data
24757      */
24758     /**
24759      * @cfg {String/HTMLElement/Element} container
24760      * container The id or element that will contain the toolbar
24761      */
24762     /**
24763      * @cfg {Boolean} displayInfo
24764      * True to display the displayMsg (defaults to false)
24765      */
24766     /**
24767      * @cfg {Number} pageSize
24768      * The number of records to display per page (defaults to 20)
24769      */
24770     pageSize: 20,
24771     /**
24772      * @cfg {String} displayMsg
24773      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24774      */
24775     displayMsg : 'Displaying {0} - {1} of {2}',
24776     /**
24777      * @cfg {String} emptyMsg
24778      * The message to display when no records are found (defaults to "No data to display")
24779      */
24780     emptyMsg : 'No data to display',
24781     /**
24782      * Customizable piece of the default paging text (defaults to "Page")
24783      * @type String
24784      */
24785     beforePageText : "Page",
24786     /**
24787      * Customizable piece of the default paging text (defaults to "of %0")
24788      * @type String
24789      */
24790     afterPageText : "of {0}",
24791     /**
24792      * Customizable piece of the default paging text (defaults to "First Page")
24793      * @type String
24794      */
24795     firstText : "First Page",
24796     /**
24797      * Customizable piece of the default paging text (defaults to "Previous Page")
24798      * @type String
24799      */
24800     prevText : "Previous Page",
24801     /**
24802      * Customizable piece of the default paging text (defaults to "Next Page")
24803      * @type String
24804      */
24805     nextText : "Next Page",
24806     /**
24807      * Customizable piece of the default paging text (defaults to "Last Page")
24808      * @type String
24809      */
24810     lastText : "Last Page",
24811     /**
24812      * Customizable piece of the default paging text (defaults to "Refresh")
24813      * @type String
24814      */
24815     refreshText : "Refresh",
24816
24817     buttons : false,
24818     // private
24819     onRender : function(ct, position) 
24820     {
24821         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24822         this.navgroup.parentId = this.id;
24823         this.navgroup.onRender(this.el, null);
24824         // add the buttons to the navgroup
24825         
24826         if(this.displayInfo){
24827             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24828             this.displayEl = this.el.select('.x-paging-info', true).first();
24829 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24830 //            this.displayEl = navel.el.select('span',true).first();
24831         }
24832         
24833         var _this = this;
24834         
24835         if(this.buttons){
24836             Roo.each(_this.buttons, function(e){ // this might need to use render????
24837                Roo.factory(e).render(_this.el);
24838             });
24839         }
24840             
24841         Roo.each(_this.toolbarItems, function(e) {
24842             _this.navgroup.addItem(e);
24843         });
24844         
24845         
24846         this.first = this.navgroup.addItem({
24847             tooltip: this.firstText,
24848             cls: "prev btn-outline-secondary",
24849             html : ' <i class="fa fa-step-backward"></i>',
24850             disabled: true,
24851             preventDefault: true,
24852             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24853         });
24854         
24855         this.prev =  this.navgroup.addItem({
24856             tooltip: this.prevText,
24857             cls: "prev btn-outline-secondary",
24858             html : ' <i class="fa fa-backward"></i>',
24859             disabled: true,
24860             preventDefault: true,
24861             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24862         });
24863     //this.addSeparator();
24864         
24865         
24866         var field = this.navgroup.addItem( {
24867             tagtype : 'span',
24868             cls : 'x-paging-position  btn-outline-secondary',
24869              disabled: true,
24870             html : this.beforePageText  +
24871                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24872                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24873          } ); //?? escaped?
24874         
24875         this.field = field.el.select('input', true).first();
24876         this.field.on("keydown", this.onPagingKeydown, this);
24877         this.field.on("focus", function(){this.dom.select();});
24878     
24879     
24880         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24881         //this.field.setHeight(18);
24882         //this.addSeparator();
24883         this.next = this.navgroup.addItem({
24884             tooltip: this.nextText,
24885             cls: "next btn-outline-secondary",
24886             html : ' <i class="fa fa-forward"></i>',
24887             disabled: true,
24888             preventDefault: true,
24889             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24890         });
24891         this.last = this.navgroup.addItem({
24892             tooltip: this.lastText,
24893             html : ' <i class="fa fa-step-forward"></i>',
24894             cls: "next btn-outline-secondary",
24895             disabled: true,
24896             preventDefault: true,
24897             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24898         });
24899     //this.addSeparator();
24900         this.loading = this.navgroup.addItem({
24901             tooltip: this.refreshText,
24902             cls: "btn-outline-secondary",
24903             html : ' <i class="fa fa-refresh"></i>',
24904             preventDefault: true,
24905             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24906         });
24907         
24908     },
24909
24910     // private
24911     updateInfo : function(){
24912         if(this.displayEl){
24913             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24914             var msg = count == 0 ?
24915                 this.emptyMsg :
24916                 String.format(
24917                     this.displayMsg,
24918                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24919                 );
24920             this.displayEl.update(msg);
24921         }
24922     },
24923
24924     // private
24925     onLoad : function(ds, r, o)
24926     {
24927         this.cursor = o.params.start ? o.params.start : 0;
24928         
24929         var d = this.getPageData(),
24930             ap = d.activePage,
24931             ps = d.pages;
24932         
24933         
24934         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24935         this.field.dom.value = ap;
24936         this.first.setDisabled(ap == 1);
24937         this.prev.setDisabled(ap == 1);
24938         this.next.setDisabled(ap == ps);
24939         this.last.setDisabled(ap == ps);
24940         this.loading.enable();
24941         this.updateInfo();
24942     },
24943
24944     // private
24945     getPageData : function(){
24946         var total = this.ds.getTotalCount();
24947         return {
24948             total : total,
24949             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24950             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24951         };
24952     },
24953
24954     // private
24955     onLoadError : function(){
24956         this.loading.enable();
24957     },
24958
24959     // private
24960     onPagingKeydown : function(e){
24961         var k = e.getKey();
24962         var d = this.getPageData();
24963         if(k == e.RETURN){
24964             var v = this.field.dom.value, pageNum;
24965             if(!v || isNaN(pageNum = parseInt(v, 10))){
24966                 this.field.dom.value = d.activePage;
24967                 return;
24968             }
24969             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24970             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24971             e.stopEvent();
24972         }
24973         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))
24974         {
24975           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24976           this.field.dom.value = pageNum;
24977           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24978           e.stopEvent();
24979         }
24980         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24981         {
24982           var v = this.field.dom.value, pageNum; 
24983           var increment = (e.shiftKey) ? 10 : 1;
24984           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24985                 increment *= -1;
24986           }
24987           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24988             this.field.dom.value = d.activePage;
24989             return;
24990           }
24991           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24992           {
24993             this.field.dom.value = parseInt(v, 10) + increment;
24994             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24995             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24996           }
24997           e.stopEvent();
24998         }
24999     },
25000
25001     // private
25002     beforeLoad : function(){
25003         if(this.loading){
25004             this.loading.disable();
25005         }
25006     },
25007
25008     // private
25009     onClick : function(which){
25010         
25011         var ds = this.ds;
25012         if (!ds) {
25013             return;
25014         }
25015         
25016         switch(which){
25017             case "first":
25018                 ds.load({params:{start: 0, limit: this.pageSize}});
25019             break;
25020             case "prev":
25021                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25022             break;
25023             case "next":
25024                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25025             break;
25026             case "last":
25027                 var total = ds.getTotalCount();
25028                 var extra = total % this.pageSize;
25029                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25030                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25031             break;
25032             case "refresh":
25033                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25034             break;
25035         }
25036     },
25037
25038     /**
25039      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25040      * @param {Roo.data.Store} store The data store to unbind
25041      */
25042     unbind : function(ds){
25043         ds.un("beforeload", this.beforeLoad, this);
25044         ds.un("load", this.onLoad, this);
25045         ds.un("loadexception", this.onLoadError, this);
25046         ds.un("remove", this.updateInfo, this);
25047         ds.un("add", this.updateInfo, this);
25048         this.ds = undefined;
25049     },
25050
25051     /**
25052      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25053      * @param {Roo.data.Store} store The data store to bind
25054      */
25055     bind : function(ds){
25056         ds.on("beforeload", this.beforeLoad, this);
25057         ds.on("load", this.onLoad, this);
25058         ds.on("loadexception", this.onLoadError, this);
25059         ds.on("remove", this.updateInfo, this);
25060         ds.on("add", this.updateInfo, this);
25061         this.ds = ds;
25062     }
25063 });/*
25064  * - LGPL
25065  *
25066  * element
25067  * 
25068  */
25069
25070 /**
25071  * @class Roo.bootstrap.MessageBar
25072  * @extends Roo.bootstrap.Component
25073  * Bootstrap MessageBar class
25074  * @cfg {String} html contents of the MessageBar
25075  * @cfg {String} weight (info | success | warning | danger) default info
25076  * @cfg {String} beforeClass insert the bar before the given class
25077  * @cfg {Boolean} closable (true | false) default false
25078  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25079  * 
25080  * @constructor
25081  * Create a new Element
25082  * @param {Object} config The config object
25083  */
25084
25085 Roo.bootstrap.MessageBar = function(config){
25086     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25087 };
25088
25089 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25090     
25091     html: '',
25092     weight: 'info',
25093     closable: false,
25094     fixed: false,
25095     beforeClass: 'bootstrap-sticky-wrap',
25096     
25097     getAutoCreate : function(){
25098         
25099         var cfg = {
25100             tag: 'div',
25101             cls: 'alert alert-dismissable alert-' + this.weight,
25102             cn: [
25103                 {
25104                     tag: 'span',
25105                     cls: 'message',
25106                     html: this.html || ''
25107                 }
25108             ]
25109         };
25110         
25111         if(this.fixed){
25112             cfg.cls += ' alert-messages-fixed';
25113         }
25114         
25115         if(this.closable){
25116             cfg.cn.push({
25117                 tag: 'button',
25118                 cls: 'close',
25119                 html: 'x'
25120             });
25121         }
25122         
25123         return cfg;
25124     },
25125     
25126     onRender : function(ct, position)
25127     {
25128         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25129         
25130         if(!this.el){
25131             var cfg = Roo.apply({},  this.getAutoCreate());
25132             cfg.id = Roo.id();
25133             
25134             if (this.cls) {
25135                 cfg.cls += ' ' + this.cls;
25136             }
25137             if (this.style) {
25138                 cfg.style = this.style;
25139             }
25140             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25141             
25142             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25143         }
25144         
25145         this.el.select('>button.close').on('click', this.hide, this);
25146         
25147     },
25148     
25149     show : function()
25150     {
25151         if (!this.rendered) {
25152             this.render();
25153         }
25154         
25155         this.el.show();
25156         
25157         this.fireEvent('show', this);
25158         
25159     },
25160     
25161     hide : function()
25162     {
25163         if (!this.rendered) {
25164             this.render();
25165         }
25166         
25167         this.el.hide();
25168         
25169         this.fireEvent('hide', this);
25170     },
25171     
25172     update : function()
25173     {
25174 //        var e = this.el.dom.firstChild;
25175 //        
25176 //        if(this.closable){
25177 //            e = e.nextSibling;
25178 //        }
25179 //        
25180 //        e.data = this.html || '';
25181
25182         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25183     }
25184    
25185 });
25186
25187  
25188
25189      /*
25190  * - LGPL
25191  *
25192  * Graph
25193  * 
25194  */
25195
25196
25197 /**
25198  * @class Roo.bootstrap.Graph
25199  * @extends Roo.bootstrap.Component
25200  * Bootstrap Graph class
25201 > Prameters
25202  -sm {number} sm 4
25203  -md {number} md 5
25204  @cfg {String} graphtype  bar | vbar | pie
25205  @cfg {number} g_x coodinator | centre x (pie)
25206  @cfg {number} g_y coodinator | centre y (pie)
25207  @cfg {number} g_r radius (pie)
25208  @cfg {number} g_height height of the chart (respected by all elements in the set)
25209  @cfg {number} g_width width of the chart (respected by all elements in the set)
25210  @cfg {Object} title The title of the chart
25211     
25212  -{Array}  values
25213  -opts (object) options for the chart 
25214      o {
25215      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25216      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25217      o vgutter (number)
25218      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.
25219      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25220      o to
25221      o stretch (boolean)
25222      o }
25223  -opts (object) options for the pie
25224      o{
25225      o cut
25226      o startAngle (number)
25227      o endAngle (number)
25228      } 
25229  *
25230  * @constructor
25231  * Create a new Input
25232  * @param {Object} config The config object
25233  */
25234
25235 Roo.bootstrap.Graph = function(config){
25236     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25237     
25238     this.addEvents({
25239         // img events
25240         /**
25241          * @event click
25242          * The img click event for the img.
25243          * @param {Roo.EventObject} e
25244          */
25245         "click" : true
25246     });
25247 };
25248
25249 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25250     
25251     sm: 4,
25252     md: 5,
25253     graphtype: 'bar',
25254     g_height: 250,
25255     g_width: 400,
25256     g_x: 50,
25257     g_y: 50,
25258     g_r: 30,
25259     opts:{
25260         //g_colors: this.colors,
25261         g_type: 'soft',
25262         g_gutter: '20%'
25263
25264     },
25265     title : false,
25266
25267     getAutoCreate : function(){
25268         
25269         var cfg = {
25270             tag: 'div',
25271             html : null
25272         };
25273         
25274         
25275         return  cfg;
25276     },
25277
25278     onRender : function(ct,position){
25279         
25280         
25281         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25282         
25283         if (typeof(Raphael) == 'undefined') {
25284             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25285             return;
25286         }
25287         
25288         this.raphael = Raphael(this.el.dom);
25289         
25290                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25291                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25292                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25293                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25294                 /*
25295                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25296                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25297                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25298                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25299                 
25300                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25301                 r.barchart(330, 10, 300, 220, data1);
25302                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25303                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25304                 */
25305                 
25306                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25307                 // r.barchart(30, 30, 560, 250,  xdata, {
25308                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25309                 //     axis : "0 0 1 1",
25310                 //     axisxlabels :  xdata
25311                 //     //yvalues : cols,
25312                    
25313                 // });
25314 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25315 //        
25316 //        this.load(null,xdata,{
25317 //                axis : "0 0 1 1",
25318 //                axisxlabels :  xdata
25319 //                });
25320
25321     },
25322
25323     load : function(graphtype,xdata,opts)
25324     {
25325         this.raphael.clear();
25326         if(!graphtype) {
25327             graphtype = this.graphtype;
25328         }
25329         if(!opts){
25330             opts = this.opts;
25331         }
25332         var r = this.raphael,
25333             fin = function () {
25334                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25335             },
25336             fout = function () {
25337                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25338             },
25339             pfin = function() {
25340                 this.sector.stop();
25341                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25342
25343                 if (this.label) {
25344                     this.label[0].stop();
25345                     this.label[0].attr({ r: 7.5 });
25346                     this.label[1].attr({ "font-weight": 800 });
25347                 }
25348             },
25349             pfout = function() {
25350                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25351
25352                 if (this.label) {
25353                     this.label[0].animate({ r: 5 }, 500, "bounce");
25354                     this.label[1].attr({ "font-weight": 400 });
25355                 }
25356             };
25357
25358         switch(graphtype){
25359             case 'bar':
25360                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25361                 break;
25362             case 'hbar':
25363                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25364                 break;
25365             case 'pie':
25366 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25367 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25368 //            
25369                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25370                 
25371                 break;
25372
25373         }
25374         
25375         if(this.title){
25376             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25377         }
25378         
25379     },
25380     
25381     setTitle: function(o)
25382     {
25383         this.title = o;
25384     },
25385     
25386     initEvents: function() {
25387         
25388         if(!this.href){
25389             this.el.on('click', this.onClick, this);
25390         }
25391     },
25392     
25393     onClick : function(e)
25394     {
25395         Roo.log('img onclick');
25396         this.fireEvent('click', this, e);
25397     }
25398    
25399 });
25400
25401  
25402 /*
25403  * - LGPL
25404  *
25405  * numberBox
25406  * 
25407  */
25408 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25409
25410 /**
25411  * @class Roo.bootstrap.dash.NumberBox
25412  * @extends Roo.bootstrap.Component
25413  * Bootstrap NumberBox class
25414  * @cfg {String} headline Box headline
25415  * @cfg {String} content Box content
25416  * @cfg {String} icon Box icon
25417  * @cfg {String} footer Footer text
25418  * @cfg {String} fhref Footer href
25419  * 
25420  * @constructor
25421  * Create a new NumberBox
25422  * @param {Object} config The config object
25423  */
25424
25425
25426 Roo.bootstrap.dash.NumberBox = function(config){
25427     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25428     
25429 };
25430
25431 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25432     
25433     headline : '',
25434     content : '',
25435     icon : '',
25436     footer : '',
25437     fhref : '',
25438     ficon : '',
25439     
25440     getAutoCreate : function(){
25441         
25442         var cfg = {
25443             tag : 'div',
25444             cls : 'small-box ',
25445             cn : [
25446                 {
25447                     tag : 'div',
25448                     cls : 'inner',
25449                     cn :[
25450                         {
25451                             tag : 'h3',
25452                             cls : 'roo-headline',
25453                             html : this.headline
25454                         },
25455                         {
25456                             tag : 'p',
25457                             cls : 'roo-content',
25458                             html : this.content
25459                         }
25460                     ]
25461                 }
25462             ]
25463         };
25464         
25465         if(this.icon){
25466             cfg.cn.push({
25467                 tag : 'div',
25468                 cls : 'icon',
25469                 cn :[
25470                     {
25471                         tag : 'i',
25472                         cls : 'ion ' + this.icon
25473                     }
25474                 ]
25475             });
25476         }
25477         
25478         if(this.footer){
25479             var footer = {
25480                 tag : 'a',
25481                 cls : 'small-box-footer',
25482                 href : this.fhref || '#',
25483                 html : this.footer
25484             };
25485             
25486             cfg.cn.push(footer);
25487             
25488         }
25489         
25490         return  cfg;
25491     },
25492
25493     onRender : function(ct,position){
25494         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25495
25496
25497        
25498                 
25499     },
25500
25501     setHeadline: function (value)
25502     {
25503         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25504     },
25505     
25506     setFooter: function (value, href)
25507     {
25508         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25509         
25510         if(href){
25511             this.el.select('a.small-box-footer',true).first().attr('href', href);
25512         }
25513         
25514     },
25515
25516     setContent: function (value)
25517     {
25518         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25519     },
25520
25521     initEvents: function() 
25522     {   
25523         
25524     }
25525     
25526 });
25527
25528  
25529 /*
25530  * - LGPL
25531  *
25532  * TabBox
25533  * 
25534  */
25535 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25536
25537 /**
25538  * @class Roo.bootstrap.dash.TabBox
25539  * @extends Roo.bootstrap.Component
25540  * Bootstrap TabBox class
25541  * @cfg {String} title Title of the TabBox
25542  * @cfg {String} icon Icon of the TabBox
25543  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25544  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25545  * 
25546  * @constructor
25547  * Create a new TabBox
25548  * @param {Object} config The config object
25549  */
25550
25551
25552 Roo.bootstrap.dash.TabBox = function(config){
25553     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25554     this.addEvents({
25555         // raw events
25556         /**
25557          * @event addpane
25558          * When a pane is added
25559          * @param {Roo.bootstrap.dash.TabPane} pane
25560          */
25561         "addpane" : true,
25562         /**
25563          * @event activatepane
25564          * When a pane is activated
25565          * @param {Roo.bootstrap.dash.TabPane} pane
25566          */
25567         "activatepane" : true
25568         
25569          
25570     });
25571     
25572     this.panes = [];
25573 };
25574
25575 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25576
25577     title : '',
25578     icon : false,
25579     showtabs : true,
25580     tabScrollable : false,
25581     
25582     getChildContainer : function()
25583     {
25584         return this.el.select('.tab-content', true).first();
25585     },
25586     
25587     getAutoCreate : function(){
25588         
25589         var header = {
25590             tag: 'li',
25591             cls: 'pull-left header',
25592             html: this.title,
25593             cn : []
25594         };
25595         
25596         if(this.icon){
25597             header.cn.push({
25598                 tag: 'i',
25599                 cls: 'fa ' + this.icon
25600             });
25601         }
25602         
25603         var h = {
25604             tag: 'ul',
25605             cls: 'nav nav-tabs pull-right',
25606             cn: [
25607                 header
25608             ]
25609         };
25610         
25611         if(this.tabScrollable){
25612             h = {
25613                 tag: 'div',
25614                 cls: 'tab-header',
25615                 cn: [
25616                     {
25617                         tag: 'ul',
25618                         cls: 'nav nav-tabs pull-right',
25619                         cn: [
25620                             header
25621                         ]
25622                     }
25623                 ]
25624             };
25625         }
25626         
25627         var cfg = {
25628             tag: 'div',
25629             cls: 'nav-tabs-custom',
25630             cn: [
25631                 h,
25632                 {
25633                     tag: 'div',
25634                     cls: 'tab-content no-padding',
25635                     cn: []
25636                 }
25637             ]
25638         };
25639
25640         return  cfg;
25641     },
25642     initEvents : function()
25643     {
25644         //Roo.log('add add pane handler');
25645         this.on('addpane', this.onAddPane, this);
25646     },
25647      /**
25648      * Updates the box title
25649      * @param {String} html to set the title to.
25650      */
25651     setTitle : function(value)
25652     {
25653         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25654     },
25655     onAddPane : function(pane)
25656     {
25657         this.panes.push(pane);
25658         //Roo.log('addpane');
25659         //Roo.log(pane);
25660         // tabs are rendere left to right..
25661         if(!this.showtabs){
25662             return;
25663         }
25664         
25665         var ctr = this.el.select('.nav-tabs', true).first();
25666          
25667          
25668         var existing = ctr.select('.nav-tab',true);
25669         var qty = existing.getCount();;
25670         
25671         
25672         var tab = ctr.createChild({
25673             tag : 'li',
25674             cls : 'nav-tab' + (qty ? '' : ' active'),
25675             cn : [
25676                 {
25677                     tag : 'a',
25678                     href:'#',
25679                     html : pane.title
25680                 }
25681             ]
25682         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25683         pane.tab = tab;
25684         
25685         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25686         if (!qty) {
25687             pane.el.addClass('active');
25688         }
25689         
25690                 
25691     },
25692     onTabClick : function(ev,un,ob,pane)
25693     {
25694         //Roo.log('tab - prev default');
25695         ev.preventDefault();
25696         
25697         
25698         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25699         pane.tab.addClass('active');
25700         //Roo.log(pane.title);
25701         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25702         // technically we should have a deactivate event.. but maybe add later.
25703         // and it should not de-activate the selected tab...
25704         this.fireEvent('activatepane', pane);
25705         pane.el.addClass('active');
25706         pane.fireEvent('activate');
25707         
25708         
25709     },
25710     
25711     getActivePane : function()
25712     {
25713         var r = false;
25714         Roo.each(this.panes, function(p) {
25715             if(p.el.hasClass('active')){
25716                 r = p;
25717                 return false;
25718             }
25719             
25720             return;
25721         });
25722         
25723         return r;
25724     }
25725     
25726     
25727 });
25728
25729  
25730 /*
25731  * - LGPL
25732  *
25733  * Tab pane
25734  * 
25735  */
25736 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25737 /**
25738  * @class Roo.bootstrap.TabPane
25739  * @extends Roo.bootstrap.Component
25740  * Bootstrap TabPane class
25741  * @cfg {Boolean} active (false | true) Default false
25742  * @cfg {String} title title of panel
25743
25744  * 
25745  * @constructor
25746  * Create a new TabPane
25747  * @param {Object} config The config object
25748  */
25749
25750 Roo.bootstrap.dash.TabPane = function(config){
25751     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25752     
25753     this.addEvents({
25754         // raw events
25755         /**
25756          * @event activate
25757          * When a pane is activated
25758          * @param {Roo.bootstrap.dash.TabPane} pane
25759          */
25760         "activate" : true
25761          
25762     });
25763 };
25764
25765 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25766     
25767     active : false,
25768     title : '',
25769     
25770     // the tabBox that this is attached to.
25771     tab : false,
25772      
25773     getAutoCreate : function() 
25774     {
25775         var cfg = {
25776             tag: 'div',
25777             cls: 'tab-pane'
25778         };
25779         
25780         if(this.active){
25781             cfg.cls += ' active';
25782         }
25783         
25784         return cfg;
25785     },
25786     initEvents  : function()
25787     {
25788         //Roo.log('trigger add pane handler');
25789         this.parent().fireEvent('addpane', this)
25790     },
25791     
25792      /**
25793      * Updates the tab title 
25794      * @param {String} html to set the title to.
25795      */
25796     setTitle: function(str)
25797     {
25798         if (!this.tab) {
25799             return;
25800         }
25801         this.title = str;
25802         this.tab.select('a', true).first().dom.innerHTML = str;
25803         
25804     }
25805     
25806     
25807     
25808 });
25809
25810  
25811
25812
25813  /*
25814  * - LGPL
25815  *
25816  * menu
25817  * 
25818  */
25819 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25820
25821 /**
25822  * @class Roo.bootstrap.menu.Menu
25823  * @extends Roo.bootstrap.Component
25824  * Bootstrap Menu class - container for Menu
25825  * @cfg {String} html Text of the menu
25826  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25827  * @cfg {String} icon Font awesome icon
25828  * @cfg {String} pos Menu align to (top | bottom) default bottom
25829  * 
25830  * 
25831  * @constructor
25832  * Create a new Menu
25833  * @param {Object} config The config object
25834  */
25835
25836
25837 Roo.bootstrap.menu.Menu = function(config){
25838     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25839     
25840     this.addEvents({
25841         /**
25842          * @event beforeshow
25843          * Fires before this menu is displayed
25844          * @param {Roo.bootstrap.menu.Menu} this
25845          */
25846         beforeshow : true,
25847         /**
25848          * @event beforehide
25849          * Fires before this menu is hidden
25850          * @param {Roo.bootstrap.menu.Menu} this
25851          */
25852         beforehide : true,
25853         /**
25854          * @event show
25855          * Fires after this menu is displayed
25856          * @param {Roo.bootstrap.menu.Menu} this
25857          */
25858         show : true,
25859         /**
25860          * @event hide
25861          * Fires after this menu is hidden
25862          * @param {Roo.bootstrap.menu.Menu} this
25863          */
25864         hide : true,
25865         /**
25866          * @event click
25867          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25868          * @param {Roo.bootstrap.menu.Menu} this
25869          * @param {Roo.EventObject} e
25870          */
25871         click : true
25872     });
25873     
25874 };
25875
25876 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25877     
25878     submenu : false,
25879     html : '',
25880     weight : 'default',
25881     icon : false,
25882     pos : 'bottom',
25883     
25884     
25885     getChildContainer : function() {
25886         if(this.isSubMenu){
25887             return this.el;
25888         }
25889         
25890         return this.el.select('ul.dropdown-menu', true).first();  
25891     },
25892     
25893     getAutoCreate : function()
25894     {
25895         var text = [
25896             {
25897                 tag : 'span',
25898                 cls : 'roo-menu-text',
25899                 html : this.html
25900             }
25901         ];
25902         
25903         if(this.icon){
25904             text.unshift({
25905                 tag : 'i',
25906                 cls : 'fa ' + this.icon
25907             })
25908         }
25909         
25910         
25911         var cfg = {
25912             tag : 'div',
25913             cls : 'btn-group',
25914             cn : [
25915                 {
25916                     tag : 'button',
25917                     cls : 'dropdown-button btn btn-' + this.weight,
25918                     cn : text
25919                 },
25920                 {
25921                     tag : 'button',
25922                     cls : 'dropdown-toggle btn btn-' + this.weight,
25923                     cn : [
25924                         {
25925                             tag : 'span',
25926                             cls : 'caret'
25927                         }
25928                     ]
25929                 },
25930                 {
25931                     tag : 'ul',
25932                     cls : 'dropdown-menu'
25933                 }
25934             ]
25935             
25936         };
25937         
25938         if(this.pos == 'top'){
25939             cfg.cls += ' dropup';
25940         }
25941         
25942         if(this.isSubMenu){
25943             cfg = {
25944                 tag : 'ul',
25945                 cls : 'dropdown-menu'
25946             }
25947         }
25948         
25949         return cfg;
25950     },
25951     
25952     onRender : function(ct, position)
25953     {
25954         this.isSubMenu = ct.hasClass('dropdown-submenu');
25955         
25956         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25957     },
25958     
25959     initEvents : function() 
25960     {
25961         if(this.isSubMenu){
25962             return;
25963         }
25964         
25965         this.hidden = true;
25966         
25967         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25968         this.triggerEl.on('click', this.onTriggerPress, this);
25969         
25970         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25971         this.buttonEl.on('click', this.onClick, this);
25972         
25973     },
25974     
25975     list : function()
25976     {
25977         if(this.isSubMenu){
25978             return this.el;
25979         }
25980         
25981         return this.el.select('ul.dropdown-menu', true).first();
25982     },
25983     
25984     onClick : function(e)
25985     {
25986         this.fireEvent("click", this, e);
25987     },
25988     
25989     onTriggerPress  : function(e)
25990     {   
25991         if (this.isVisible()) {
25992             this.hide();
25993         } else {
25994             this.show();
25995         }
25996     },
25997     
25998     isVisible : function(){
25999         return !this.hidden;
26000     },
26001     
26002     show : function()
26003     {
26004         this.fireEvent("beforeshow", this);
26005         
26006         this.hidden = false;
26007         this.el.addClass('open');
26008         
26009         Roo.get(document).on("mouseup", this.onMouseUp, this);
26010         
26011         this.fireEvent("show", this);
26012         
26013         
26014     },
26015     
26016     hide : function()
26017     {
26018         this.fireEvent("beforehide", this);
26019         
26020         this.hidden = true;
26021         this.el.removeClass('open');
26022         
26023         Roo.get(document).un("mouseup", this.onMouseUp);
26024         
26025         this.fireEvent("hide", this);
26026     },
26027     
26028     onMouseUp : function()
26029     {
26030         this.hide();
26031     }
26032     
26033 });
26034
26035  
26036  /*
26037  * - LGPL
26038  *
26039  * menu item
26040  * 
26041  */
26042 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26043
26044 /**
26045  * @class Roo.bootstrap.menu.Item
26046  * @extends Roo.bootstrap.Component
26047  * Bootstrap MenuItem class
26048  * @cfg {Boolean} submenu (true | false) default false
26049  * @cfg {String} html text of the item
26050  * @cfg {String} href the link
26051  * @cfg {Boolean} disable (true | false) default false
26052  * @cfg {Boolean} preventDefault (true | false) default true
26053  * @cfg {String} icon Font awesome icon
26054  * @cfg {String} pos Submenu align to (left | right) default right 
26055  * 
26056  * 
26057  * @constructor
26058  * Create a new Item
26059  * @param {Object} config The config object
26060  */
26061
26062
26063 Roo.bootstrap.menu.Item = function(config){
26064     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26065     this.addEvents({
26066         /**
26067          * @event mouseover
26068          * Fires when the mouse is hovering over this menu
26069          * @param {Roo.bootstrap.menu.Item} this
26070          * @param {Roo.EventObject} e
26071          */
26072         mouseover : true,
26073         /**
26074          * @event mouseout
26075          * Fires when the mouse exits this menu
26076          * @param {Roo.bootstrap.menu.Item} this
26077          * @param {Roo.EventObject} e
26078          */
26079         mouseout : true,
26080         // raw events
26081         /**
26082          * @event click
26083          * The raw click event for the entire grid.
26084          * @param {Roo.EventObject} e
26085          */
26086         click : true
26087     });
26088 };
26089
26090 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26091     
26092     submenu : false,
26093     href : '',
26094     html : '',
26095     preventDefault: true,
26096     disable : false,
26097     icon : false,
26098     pos : 'right',
26099     
26100     getAutoCreate : function()
26101     {
26102         var text = [
26103             {
26104                 tag : 'span',
26105                 cls : 'roo-menu-item-text',
26106                 html : this.html
26107             }
26108         ];
26109         
26110         if(this.icon){
26111             text.unshift({
26112                 tag : 'i',
26113                 cls : 'fa ' + this.icon
26114             })
26115         }
26116         
26117         var cfg = {
26118             tag : 'li',
26119             cn : [
26120                 {
26121                     tag : 'a',
26122                     href : this.href || '#',
26123                     cn : text
26124                 }
26125             ]
26126         };
26127         
26128         if(this.disable){
26129             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26130         }
26131         
26132         if(this.submenu){
26133             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26134             
26135             if(this.pos == 'left'){
26136                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26137             }
26138         }
26139         
26140         return cfg;
26141     },
26142     
26143     initEvents : function() 
26144     {
26145         this.el.on('mouseover', this.onMouseOver, this);
26146         this.el.on('mouseout', this.onMouseOut, this);
26147         
26148         this.el.select('a', true).first().on('click', this.onClick, this);
26149         
26150     },
26151     
26152     onClick : function(e)
26153     {
26154         if(this.preventDefault){
26155             e.preventDefault();
26156         }
26157         
26158         this.fireEvent("click", this, e);
26159     },
26160     
26161     onMouseOver : function(e)
26162     {
26163         if(this.submenu && this.pos == 'left'){
26164             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26165         }
26166         
26167         this.fireEvent("mouseover", this, e);
26168     },
26169     
26170     onMouseOut : function(e)
26171     {
26172         this.fireEvent("mouseout", this, e);
26173     }
26174 });
26175
26176  
26177
26178  /*
26179  * - LGPL
26180  *
26181  * menu separator
26182  * 
26183  */
26184 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26185
26186 /**
26187  * @class Roo.bootstrap.menu.Separator
26188  * @extends Roo.bootstrap.Component
26189  * Bootstrap Separator class
26190  * 
26191  * @constructor
26192  * Create a new Separator
26193  * @param {Object} config The config object
26194  */
26195
26196
26197 Roo.bootstrap.menu.Separator = function(config){
26198     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26199 };
26200
26201 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26202     
26203     getAutoCreate : function(){
26204         var cfg = {
26205             tag : 'li',
26206             cls: 'divider'
26207         };
26208         
26209         return cfg;
26210     }
26211    
26212 });
26213
26214  
26215
26216  /*
26217  * - LGPL
26218  *
26219  * Tooltip
26220  * 
26221  */
26222
26223 /**
26224  * @class Roo.bootstrap.Tooltip
26225  * Bootstrap Tooltip class
26226  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26227  * to determine which dom element triggers the tooltip.
26228  * 
26229  * It needs to add support for additional attributes like tooltip-position
26230  * 
26231  * @constructor
26232  * Create a new Toolti
26233  * @param {Object} config The config object
26234  */
26235
26236 Roo.bootstrap.Tooltip = function(config){
26237     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26238     
26239     this.alignment = Roo.bootstrap.Tooltip.alignment;
26240     
26241     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26242         this.alignment = config.alignment;
26243     }
26244     
26245 };
26246
26247 Roo.apply(Roo.bootstrap.Tooltip, {
26248     /**
26249      * @function init initialize tooltip monitoring.
26250      * @static
26251      */
26252     currentEl : false,
26253     currentTip : false,
26254     currentRegion : false,
26255     
26256     //  init : delay?
26257     
26258     init : function()
26259     {
26260         Roo.get(document).on('mouseover', this.enter ,this);
26261         Roo.get(document).on('mouseout', this.leave, this);
26262          
26263         
26264         this.currentTip = new Roo.bootstrap.Tooltip();
26265     },
26266     
26267     enter : function(ev)
26268     {
26269         var dom = ev.getTarget();
26270         
26271         //Roo.log(['enter',dom]);
26272         var el = Roo.fly(dom);
26273         if (this.currentEl) {
26274             //Roo.log(dom);
26275             //Roo.log(this.currentEl);
26276             //Roo.log(this.currentEl.contains(dom));
26277             if (this.currentEl == el) {
26278                 return;
26279             }
26280             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26281                 return;
26282             }
26283
26284         }
26285         
26286         if (this.currentTip.el) {
26287             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26288         }    
26289         //Roo.log(ev);
26290         
26291         if(!el || el.dom == document){
26292             return;
26293         }
26294         
26295         var bindEl = el;
26296         
26297         // you can not look for children, as if el is the body.. then everythign is the child..
26298         if (!el.attr('tooltip')) { //
26299             if (!el.select("[tooltip]").elements.length) {
26300                 return;
26301             }
26302             // is the mouse over this child...?
26303             bindEl = el.select("[tooltip]").first();
26304             var xy = ev.getXY();
26305             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26306                 //Roo.log("not in region.");
26307                 return;
26308             }
26309             //Roo.log("child element over..");
26310             
26311         }
26312         this.currentEl = bindEl;
26313         this.currentTip.bind(bindEl);
26314         this.currentRegion = Roo.lib.Region.getRegion(dom);
26315         this.currentTip.enter();
26316         
26317     },
26318     leave : function(ev)
26319     {
26320         var dom = ev.getTarget();
26321         //Roo.log(['leave',dom]);
26322         if (!this.currentEl) {
26323             return;
26324         }
26325         
26326         
26327         if (dom != this.currentEl.dom) {
26328             return;
26329         }
26330         var xy = ev.getXY();
26331         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26332             return;
26333         }
26334         // only activate leave if mouse cursor is outside... bounding box..
26335         
26336         
26337         
26338         
26339         if (this.currentTip) {
26340             this.currentTip.leave();
26341         }
26342         //Roo.log('clear currentEl');
26343         this.currentEl = false;
26344         
26345         
26346     },
26347     alignment : {
26348         'left' : ['r-l', [-2,0], 'right'],
26349         'right' : ['l-r', [2,0], 'left'],
26350         'bottom' : ['t-b', [0,2], 'top'],
26351         'top' : [ 'b-t', [0,-2], 'bottom']
26352     }
26353     
26354 });
26355
26356
26357 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26358     
26359     
26360     bindEl : false,
26361     
26362     delay : null, // can be { show : 300 , hide: 500}
26363     
26364     timeout : null,
26365     
26366     hoverState : null, //???
26367     
26368     placement : 'bottom', 
26369     
26370     alignment : false,
26371     
26372     getAutoCreate : function(){
26373     
26374         var cfg = {
26375            cls : 'tooltip',
26376            role : 'tooltip',
26377            cn : [
26378                 {
26379                     cls : 'tooltip-arrow'
26380                 },
26381                 {
26382                     cls : 'tooltip-inner'
26383                 }
26384            ]
26385         };
26386         
26387         return cfg;
26388     },
26389     bind : function(el)
26390     {
26391         this.bindEl = el;
26392     },
26393       
26394     
26395     enter : function () {
26396        
26397         if (this.timeout != null) {
26398             clearTimeout(this.timeout);
26399         }
26400         
26401         this.hoverState = 'in';
26402          //Roo.log("enter - show");
26403         if (!this.delay || !this.delay.show) {
26404             this.show();
26405             return;
26406         }
26407         var _t = this;
26408         this.timeout = setTimeout(function () {
26409             if (_t.hoverState == 'in') {
26410                 _t.show();
26411             }
26412         }, this.delay.show);
26413     },
26414     leave : function()
26415     {
26416         clearTimeout(this.timeout);
26417     
26418         this.hoverState = 'out';
26419          if (!this.delay || !this.delay.hide) {
26420             this.hide();
26421             return;
26422         }
26423        
26424         var _t = this;
26425         this.timeout = setTimeout(function () {
26426             //Roo.log("leave - timeout");
26427             
26428             if (_t.hoverState == 'out') {
26429                 _t.hide();
26430                 Roo.bootstrap.Tooltip.currentEl = false;
26431             }
26432         }, delay);
26433     },
26434     
26435     show : function (msg)
26436     {
26437         if (!this.el) {
26438             this.render(document.body);
26439         }
26440         // set content.
26441         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26442         
26443         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26444         
26445         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26446         
26447         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26448         
26449         var placement = typeof this.placement == 'function' ?
26450             this.placement.call(this, this.el, on_el) :
26451             this.placement;
26452             
26453         var autoToken = /\s?auto?\s?/i;
26454         var autoPlace = autoToken.test(placement);
26455         if (autoPlace) {
26456             placement = placement.replace(autoToken, '') || 'top';
26457         }
26458         
26459         //this.el.detach()
26460         //this.el.setXY([0,0]);
26461         this.el.show();
26462         //this.el.dom.style.display='block';
26463         
26464         //this.el.appendTo(on_el);
26465         
26466         var p = this.getPosition();
26467         var box = this.el.getBox();
26468         
26469         if (autoPlace) {
26470             // fixme..
26471         }
26472         
26473         var align = this.alignment[placement];
26474         
26475         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26476         
26477         if(placement == 'top' || placement == 'bottom'){
26478             if(xy[0] < 0){
26479                 placement = 'right';
26480             }
26481             
26482             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26483                 placement = 'left';
26484             }
26485             
26486             var scroll = Roo.select('body', true).first().getScroll();
26487             
26488             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26489                 placement = 'top';
26490             }
26491             
26492             align = this.alignment[placement];
26493         }
26494         
26495         this.el.alignTo(this.bindEl, align[0],align[1]);
26496         //var arrow = this.el.select('.arrow',true).first();
26497         //arrow.set(align[2], 
26498         
26499         this.el.addClass(placement);
26500         
26501         this.el.addClass('in fade');
26502         
26503         this.hoverState = null;
26504         
26505         if (this.el.hasClass('fade')) {
26506             // fade it?
26507         }
26508         
26509     },
26510     hide : function()
26511     {
26512          
26513         if (!this.el) {
26514             return;
26515         }
26516         //this.el.setXY([0,0]);
26517         this.el.removeClass('in');
26518         //this.el.hide();
26519         
26520     }
26521     
26522 });
26523  
26524
26525  /*
26526  * - LGPL
26527  *
26528  * Location Picker
26529  * 
26530  */
26531
26532 /**
26533  * @class Roo.bootstrap.LocationPicker
26534  * @extends Roo.bootstrap.Component
26535  * Bootstrap LocationPicker class
26536  * @cfg {Number} latitude Position when init default 0
26537  * @cfg {Number} longitude Position when init default 0
26538  * @cfg {Number} zoom default 15
26539  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26540  * @cfg {Boolean} mapTypeControl default false
26541  * @cfg {Boolean} disableDoubleClickZoom default false
26542  * @cfg {Boolean} scrollwheel default true
26543  * @cfg {Boolean} streetViewControl default false
26544  * @cfg {Number} radius default 0
26545  * @cfg {String} locationName
26546  * @cfg {Boolean} draggable default true
26547  * @cfg {Boolean} enableAutocomplete default false
26548  * @cfg {Boolean} enableReverseGeocode default true
26549  * @cfg {String} markerTitle
26550  * 
26551  * @constructor
26552  * Create a new LocationPicker
26553  * @param {Object} config The config object
26554  */
26555
26556
26557 Roo.bootstrap.LocationPicker = function(config){
26558     
26559     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26560     
26561     this.addEvents({
26562         /**
26563          * @event initial
26564          * Fires when the picker initialized.
26565          * @param {Roo.bootstrap.LocationPicker} this
26566          * @param {Google Location} location
26567          */
26568         initial : true,
26569         /**
26570          * @event positionchanged
26571          * Fires when the picker position changed.
26572          * @param {Roo.bootstrap.LocationPicker} this
26573          * @param {Google Location} location
26574          */
26575         positionchanged : true,
26576         /**
26577          * @event resize
26578          * Fires when the map resize.
26579          * @param {Roo.bootstrap.LocationPicker} this
26580          */
26581         resize : true,
26582         /**
26583          * @event show
26584          * Fires when the map show.
26585          * @param {Roo.bootstrap.LocationPicker} this
26586          */
26587         show : true,
26588         /**
26589          * @event hide
26590          * Fires when the map hide.
26591          * @param {Roo.bootstrap.LocationPicker} this
26592          */
26593         hide : true,
26594         /**
26595          * @event mapClick
26596          * Fires when click the map.
26597          * @param {Roo.bootstrap.LocationPicker} this
26598          * @param {Map event} e
26599          */
26600         mapClick : true,
26601         /**
26602          * @event mapRightClick
26603          * Fires when right click the map.
26604          * @param {Roo.bootstrap.LocationPicker} this
26605          * @param {Map event} e
26606          */
26607         mapRightClick : true,
26608         /**
26609          * @event markerClick
26610          * Fires when click the marker.
26611          * @param {Roo.bootstrap.LocationPicker} this
26612          * @param {Map event} e
26613          */
26614         markerClick : true,
26615         /**
26616          * @event markerRightClick
26617          * Fires when right click the marker.
26618          * @param {Roo.bootstrap.LocationPicker} this
26619          * @param {Map event} e
26620          */
26621         markerRightClick : true,
26622         /**
26623          * @event OverlayViewDraw
26624          * Fires when OverlayView Draw
26625          * @param {Roo.bootstrap.LocationPicker} this
26626          */
26627         OverlayViewDraw : true,
26628         /**
26629          * @event OverlayViewOnAdd
26630          * Fires when OverlayView Draw
26631          * @param {Roo.bootstrap.LocationPicker} this
26632          */
26633         OverlayViewOnAdd : true,
26634         /**
26635          * @event OverlayViewOnRemove
26636          * Fires when OverlayView Draw
26637          * @param {Roo.bootstrap.LocationPicker} this
26638          */
26639         OverlayViewOnRemove : true,
26640         /**
26641          * @event OverlayViewShow
26642          * Fires when OverlayView Draw
26643          * @param {Roo.bootstrap.LocationPicker} this
26644          * @param {Pixel} cpx
26645          */
26646         OverlayViewShow : true,
26647         /**
26648          * @event OverlayViewHide
26649          * Fires when OverlayView Draw
26650          * @param {Roo.bootstrap.LocationPicker} this
26651          */
26652         OverlayViewHide : true,
26653         /**
26654          * @event loadexception
26655          * Fires when load google lib failed.
26656          * @param {Roo.bootstrap.LocationPicker} this
26657          */
26658         loadexception : true
26659     });
26660         
26661 };
26662
26663 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26664     
26665     gMapContext: false,
26666     
26667     latitude: 0,
26668     longitude: 0,
26669     zoom: 15,
26670     mapTypeId: false,
26671     mapTypeControl: false,
26672     disableDoubleClickZoom: false,
26673     scrollwheel: true,
26674     streetViewControl: false,
26675     radius: 0,
26676     locationName: '',
26677     draggable: true,
26678     enableAutocomplete: false,
26679     enableReverseGeocode: true,
26680     markerTitle: '',
26681     
26682     getAutoCreate: function()
26683     {
26684
26685         var cfg = {
26686             tag: 'div',
26687             cls: 'roo-location-picker'
26688         };
26689         
26690         return cfg
26691     },
26692     
26693     initEvents: function(ct, position)
26694     {       
26695         if(!this.el.getWidth() || this.isApplied()){
26696             return;
26697         }
26698         
26699         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26700         
26701         this.initial();
26702     },
26703     
26704     initial: function()
26705     {
26706         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26707             this.fireEvent('loadexception', this);
26708             return;
26709         }
26710         
26711         if(!this.mapTypeId){
26712             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26713         }
26714         
26715         this.gMapContext = this.GMapContext();
26716         
26717         this.initOverlayView();
26718         
26719         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26720         
26721         var _this = this;
26722                 
26723         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26724             _this.setPosition(_this.gMapContext.marker.position);
26725         });
26726         
26727         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26728             _this.fireEvent('mapClick', this, event);
26729             
26730         });
26731
26732         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26733             _this.fireEvent('mapRightClick', this, event);
26734             
26735         });
26736         
26737         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26738             _this.fireEvent('markerClick', this, event);
26739             
26740         });
26741
26742         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26743             _this.fireEvent('markerRightClick', this, event);
26744             
26745         });
26746         
26747         this.setPosition(this.gMapContext.location);
26748         
26749         this.fireEvent('initial', this, this.gMapContext.location);
26750     },
26751     
26752     initOverlayView: function()
26753     {
26754         var _this = this;
26755         
26756         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26757             
26758             draw: function()
26759             {
26760                 _this.fireEvent('OverlayViewDraw', _this);
26761             },
26762             
26763             onAdd: function()
26764             {
26765                 _this.fireEvent('OverlayViewOnAdd', _this);
26766             },
26767             
26768             onRemove: function()
26769             {
26770                 _this.fireEvent('OverlayViewOnRemove', _this);
26771             },
26772             
26773             show: function(cpx)
26774             {
26775                 _this.fireEvent('OverlayViewShow', _this, cpx);
26776             },
26777             
26778             hide: function()
26779             {
26780                 _this.fireEvent('OverlayViewHide', _this);
26781             }
26782             
26783         });
26784     },
26785     
26786     fromLatLngToContainerPixel: function(event)
26787     {
26788         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26789     },
26790     
26791     isApplied: function() 
26792     {
26793         return this.getGmapContext() == false ? false : true;
26794     },
26795     
26796     getGmapContext: function() 
26797     {
26798         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26799     },
26800     
26801     GMapContext: function() 
26802     {
26803         var position = new google.maps.LatLng(this.latitude, this.longitude);
26804         
26805         var _map = new google.maps.Map(this.el.dom, {
26806             center: position,
26807             zoom: this.zoom,
26808             mapTypeId: this.mapTypeId,
26809             mapTypeControl: this.mapTypeControl,
26810             disableDoubleClickZoom: this.disableDoubleClickZoom,
26811             scrollwheel: this.scrollwheel,
26812             streetViewControl: this.streetViewControl,
26813             locationName: this.locationName,
26814             draggable: this.draggable,
26815             enableAutocomplete: this.enableAutocomplete,
26816             enableReverseGeocode: this.enableReverseGeocode
26817         });
26818         
26819         var _marker = new google.maps.Marker({
26820             position: position,
26821             map: _map,
26822             title: this.markerTitle,
26823             draggable: this.draggable
26824         });
26825         
26826         return {
26827             map: _map,
26828             marker: _marker,
26829             circle: null,
26830             location: position,
26831             radius: this.radius,
26832             locationName: this.locationName,
26833             addressComponents: {
26834                 formatted_address: null,
26835                 addressLine1: null,
26836                 addressLine2: null,
26837                 streetName: null,
26838                 streetNumber: null,
26839                 city: null,
26840                 district: null,
26841                 state: null,
26842                 stateOrProvince: null
26843             },
26844             settings: this,
26845             domContainer: this.el.dom,
26846             geodecoder: new google.maps.Geocoder()
26847         };
26848     },
26849     
26850     drawCircle: function(center, radius, options) 
26851     {
26852         if (this.gMapContext.circle != null) {
26853             this.gMapContext.circle.setMap(null);
26854         }
26855         if (radius > 0) {
26856             radius *= 1;
26857             options = Roo.apply({}, options, {
26858                 strokeColor: "#0000FF",
26859                 strokeOpacity: .35,
26860                 strokeWeight: 2,
26861                 fillColor: "#0000FF",
26862                 fillOpacity: .2
26863             });
26864             
26865             options.map = this.gMapContext.map;
26866             options.radius = radius;
26867             options.center = center;
26868             this.gMapContext.circle = new google.maps.Circle(options);
26869             return this.gMapContext.circle;
26870         }
26871         
26872         return null;
26873     },
26874     
26875     setPosition: function(location) 
26876     {
26877         this.gMapContext.location = location;
26878         this.gMapContext.marker.setPosition(location);
26879         this.gMapContext.map.panTo(location);
26880         this.drawCircle(location, this.gMapContext.radius, {});
26881         
26882         var _this = this;
26883         
26884         if (this.gMapContext.settings.enableReverseGeocode) {
26885             this.gMapContext.geodecoder.geocode({
26886                 latLng: this.gMapContext.location
26887             }, function(results, status) {
26888                 
26889                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26890                     _this.gMapContext.locationName = results[0].formatted_address;
26891                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26892                     
26893                     _this.fireEvent('positionchanged', this, location);
26894                 }
26895             });
26896             
26897             return;
26898         }
26899         
26900         this.fireEvent('positionchanged', this, location);
26901     },
26902     
26903     resize: function()
26904     {
26905         google.maps.event.trigger(this.gMapContext.map, "resize");
26906         
26907         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26908         
26909         this.fireEvent('resize', this);
26910     },
26911     
26912     setPositionByLatLng: function(latitude, longitude)
26913     {
26914         this.setPosition(new google.maps.LatLng(latitude, longitude));
26915     },
26916     
26917     getCurrentPosition: function() 
26918     {
26919         return {
26920             latitude: this.gMapContext.location.lat(),
26921             longitude: this.gMapContext.location.lng()
26922         };
26923     },
26924     
26925     getAddressName: function() 
26926     {
26927         return this.gMapContext.locationName;
26928     },
26929     
26930     getAddressComponents: function() 
26931     {
26932         return this.gMapContext.addressComponents;
26933     },
26934     
26935     address_component_from_google_geocode: function(address_components) 
26936     {
26937         var result = {};
26938         
26939         for (var i = 0; i < address_components.length; i++) {
26940             var component = address_components[i];
26941             if (component.types.indexOf("postal_code") >= 0) {
26942                 result.postalCode = component.short_name;
26943             } else if (component.types.indexOf("street_number") >= 0) {
26944                 result.streetNumber = component.short_name;
26945             } else if (component.types.indexOf("route") >= 0) {
26946                 result.streetName = component.short_name;
26947             } else if (component.types.indexOf("neighborhood") >= 0) {
26948                 result.city = component.short_name;
26949             } else if (component.types.indexOf("locality") >= 0) {
26950                 result.city = component.short_name;
26951             } else if (component.types.indexOf("sublocality") >= 0) {
26952                 result.district = component.short_name;
26953             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26954                 result.stateOrProvince = component.short_name;
26955             } else if (component.types.indexOf("country") >= 0) {
26956                 result.country = component.short_name;
26957             }
26958         }
26959         
26960         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26961         result.addressLine2 = "";
26962         return result;
26963     },
26964     
26965     setZoomLevel: function(zoom)
26966     {
26967         this.gMapContext.map.setZoom(zoom);
26968     },
26969     
26970     show: function()
26971     {
26972         if(!this.el){
26973             return;
26974         }
26975         
26976         this.el.show();
26977         
26978         this.resize();
26979         
26980         this.fireEvent('show', this);
26981     },
26982     
26983     hide: function()
26984     {
26985         if(!this.el){
26986             return;
26987         }
26988         
26989         this.el.hide();
26990         
26991         this.fireEvent('hide', this);
26992     }
26993     
26994 });
26995
26996 Roo.apply(Roo.bootstrap.LocationPicker, {
26997     
26998     OverlayView : function(map, options)
26999     {
27000         options = options || {};
27001         
27002         this.setMap(map);
27003     }
27004     
27005     
27006 });/*
27007  * - LGPL
27008  *
27009  * Alert
27010  * 
27011  */
27012
27013 /**
27014  * @class Roo.bootstrap.Alert
27015  * @extends Roo.bootstrap.Component
27016  * Bootstrap Alert class
27017  * @cfg {String} title The title of alert
27018  * @cfg {String} html The content of alert
27019  * @cfg {String} weight (  success | info | warning | danger )
27020  * @cfg {String} faicon font-awesomeicon
27021  * 
27022  * @constructor
27023  * Create a new alert
27024  * @param {Object} config The config object
27025  */
27026
27027
27028 Roo.bootstrap.Alert = function(config){
27029     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27030     
27031 };
27032
27033 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27034     
27035     title: '',
27036     html: '',
27037     weight: false,
27038     faicon: false,
27039     
27040     getAutoCreate : function()
27041     {
27042         
27043         var cfg = {
27044             tag : 'div',
27045             cls : 'alert',
27046             cn : [
27047                 {
27048                     tag : 'i',
27049                     cls : 'roo-alert-icon'
27050                     
27051                 },
27052                 {
27053                     tag : 'b',
27054                     cls : 'roo-alert-title',
27055                     html : this.title
27056                 },
27057                 {
27058                     tag : 'span',
27059                     cls : 'roo-alert-text',
27060                     html : this.html
27061                 }
27062             ]
27063         };
27064         
27065         if(this.faicon){
27066             cfg.cn[0].cls += ' fa ' + this.faicon;
27067         }
27068         
27069         if(this.weight){
27070             cfg.cls += ' alert-' + this.weight;
27071         }
27072         
27073         return cfg;
27074     },
27075     
27076     initEvents: function() 
27077     {
27078         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27079     },
27080     
27081     setTitle : function(str)
27082     {
27083         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27084     },
27085     
27086     setText : function(str)
27087     {
27088         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27089     },
27090     
27091     setWeight : function(weight)
27092     {
27093         if(this.weight){
27094             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27095         }
27096         
27097         this.weight = weight;
27098         
27099         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27100     },
27101     
27102     setIcon : function(icon)
27103     {
27104         if(this.faicon){
27105             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27106         }
27107         
27108         this.faicon = icon;
27109         
27110         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27111     },
27112     
27113     hide: function() 
27114     {
27115         this.el.hide();   
27116     },
27117     
27118     show: function() 
27119     {  
27120         this.el.show();   
27121     }
27122     
27123 });
27124
27125  
27126 /*
27127 * Licence: LGPL
27128 */
27129
27130 /**
27131  * @class Roo.bootstrap.UploadCropbox
27132  * @extends Roo.bootstrap.Component
27133  * Bootstrap UploadCropbox class
27134  * @cfg {String} emptyText show when image has been loaded
27135  * @cfg {String} rotateNotify show when image too small to rotate
27136  * @cfg {Number} errorTimeout default 3000
27137  * @cfg {Number} minWidth default 300
27138  * @cfg {Number} minHeight default 300
27139  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27140  * @cfg {Boolean} isDocument (true|false) default false
27141  * @cfg {String} url action url
27142  * @cfg {String} paramName default 'imageUpload'
27143  * @cfg {String} method default POST
27144  * @cfg {Boolean} loadMask (true|false) default true
27145  * @cfg {Boolean} loadingText default 'Loading...'
27146  * 
27147  * @constructor
27148  * Create a new UploadCropbox
27149  * @param {Object} config The config object
27150  */
27151
27152 Roo.bootstrap.UploadCropbox = function(config){
27153     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27154     
27155     this.addEvents({
27156         /**
27157          * @event beforeselectfile
27158          * Fire before select file
27159          * @param {Roo.bootstrap.UploadCropbox} this
27160          */
27161         "beforeselectfile" : true,
27162         /**
27163          * @event initial
27164          * Fire after initEvent
27165          * @param {Roo.bootstrap.UploadCropbox} this
27166          */
27167         "initial" : true,
27168         /**
27169          * @event crop
27170          * Fire after initEvent
27171          * @param {Roo.bootstrap.UploadCropbox} this
27172          * @param {String} data
27173          */
27174         "crop" : true,
27175         /**
27176          * @event prepare
27177          * Fire when preparing the file data
27178          * @param {Roo.bootstrap.UploadCropbox} this
27179          * @param {Object} file
27180          */
27181         "prepare" : true,
27182         /**
27183          * @event exception
27184          * Fire when get exception
27185          * @param {Roo.bootstrap.UploadCropbox} this
27186          * @param {XMLHttpRequest} xhr
27187          */
27188         "exception" : true,
27189         /**
27190          * @event beforeloadcanvas
27191          * Fire before load the canvas
27192          * @param {Roo.bootstrap.UploadCropbox} this
27193          * @param {String} src
27194          */
27195         "beforeloadcanvas" : true,
27196         /**
27197          * @event trash
27198          * Fire when trash image
27199          * @param {Roo.bootstrap.UploadCropbox} this
27200          */
27201         "trash" : true,
27202         /**
27203          * @event download
27204          * Fire when download the image
27205          * @param {Roo.bootstrap.UploadCropbox} this
27206          */
27207         "download" : true,
27208         /**
27209          * @event footerbuttonclick
27210          * Fire when footerbuttonclick
27211          * @param {Roo.bootstrap.UploadCropbox} this
27212          * @param {String} type
27213          */
27214         "footerbuttonclick" : true,
27215         /**
27216          * @event resize
27217          * Fire when resize
27218          * @param {Roo.bootstrap.UploadCropbox} this
27219          */
27220         "resize" : true,
27221         /**
27222          * @event rotate
27223          * Fire when rotate the image
27224          * @param {Roo.bootstrap.UploadCropbox} this
27225          * @param {String} pos
27226          */
27227         "rotate" : true,
27228         /**
27229          * @event inspect
27230          * Fire when inspect the file
27231          * @param {Roo.bootstrap.UploadCropbox} this
27232          * @param {Object} file
27233          */
27234         "inspect" : true,
27235         /**
27236          * @event upload
27237          * Fire when xhr upload the file
27238          * @param {Roo.bootstrap.UploadCropbox} this
27239          * @param {Object} data
27240          */
27241         "upload" : true,
27242         /**
27243          * @event arrange
27244          * Fire when arrange the file data
27245          * @param {Roo.bootstrap.UploadCropbox} this
27246          * @param {Object} formData
27247          */
27248         "arrange" : true
27249     });
27250     
27251     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27252 };
27253
27254 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27255     
27256     emptyText : 'Click to upload image',
27257     rotateNotify : 'Image is too small to rotate',
27258     errorTimeout : 3000,
27259     scale : 0,
27260     baseScale : 1,
27261     rotate : 0,
27262     dragable : false,
27263     pinching : false,
27264     mouseX : 0,
27265     mouseY : 0,
27266     cropData : false,
27267     minWidth : 300,
27268     minHeight : 300,
27269     file : false,
27270     exif : {},
27271     baseRotate : 1,
27272     cropType : 'image/jpeg',
27273     buttons : false,
27274     canvasLoaded : false,
27275     isDocument : false,
27276     method : 'POST',
27277     paramName : 'imageUpload',
27278     loadMask : true,
27279     loadingText : 'Loading...',
27280     maskEl : false,
27281     
27282     getAutoCreate : function()
27283     {
27284         var cfg = {
27285             tag : 'div',
27286             cls : 'roo-upload-cropbox',
27287             cn : [
27288                 {
27289                     tag : 'input',
27290                     cls : 'roo-upload-cropbox-selector',
27291                     type : 'file'
27292                 },
27293                 {
27294                     tag : 'div',
27295                     cls : 'roo-upload-cropbox-body',
27296                     style : 'cursor:pointer',
27297                     cn : [
27298                         {
27299                             tag : 'div',
27300                             cls : 'roo-upload-cropbox-preview'
27301                         },
27302                         {
27303                             tag : 'div',
27304                             cls : 'roo-upload-cropbox-thumb'
27305                         },
27306                         {
27307                             tag : 'div',
27308                             cls : 'roo-upload-cropbox-empty-notify',
27309                             html : this.emptyText
27310                         },
27311                         {
27312                             tag : 'div',
27313                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27314                             html : this.rotateNotify
27315                         }
27316                     ]
27317                 },
27318                 {
27319                     tag : 'div',
27320                     cls : 'roo-upload-cropbox-footer',
27321                     cn : {
27322                         tag : 'div',
27323                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27324                         cn : []
27325                     }
27326                 }
27327             ]
27328         };
27329         
27330         return cfg;
27331     },
27332     
27333     onRender : function(ct, position)
27334     {
27335         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27336         
27337         if (this.buttons.length) {
27338             
27339             Roo.each(this.buttons, function(bb) {
27340                 
27341                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27342                 
27343                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27344                 
27345             }, this);
27346         }
27347         
27348         if(this.loadMask){
27349             this.maskEl = this.el;
27350         }
27351     },
27352     
27353     initEvents : function()
27354     {
27355         this.urlAPI = (window.createObjectURL && window) || 
27356                                 (window.URL && URL.revokeObjectURL && URL) || 
27357                                 (window.webkitURL && webkitURL);
27358                         
27359         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27360         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27361         
27362         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27363         this.selectorEl.hide();
27364         
27365         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27366         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27367         
27368         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27369         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27370         this.thumbEl.hide();
27371         
27372         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27373         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27374         
27375         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27376         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27377         this.errorEl.hide();
27378         
27379         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27380         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27381         this.footerEl.hide();
27382         
27383         this.setThumbBoxSize();
27384         
27385         this.bind();
27386         
27387         this.resize();
27388         
27389         this.fireEvent('initial', this);
27390     },
27391
27392     bind : function()
27393     {
27394         var _this = this;
27395         
27396         window.addEventListener("resize", function() { _this.resize(); } );
27397         
27398         this.bodyEl.on('click', this.beforeSelectFile, this);
27399         
27400         if(Roo.isTouch){
27401             this.bodyEl.on('touchstart', this.onTouchStart, this);
27402             this.bodyEl.on('touchmove', this.onTouchMove, this);
27403             this.bodyEl.on('touchend', this.onTouchEnd, this);
27404         }
27405         
27406         if(!Roo.isTouch){
27407             this.bodyEl.on('mousedown', this.onMouseDown, this);
27408             this.bodyEl.on('mousemove', this.onMouseMove, this);
27409             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27410             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27411             Roo.get(document).on('mouseup', this.onMouseUp, this);
27412         }
27413         
27414         this.selectorEl.on('change', this.onFileSelected, this);
27415     },
27416     
27417     reset : function()
27418     {    
27419         this.scale = 0;
27420         this.baseScale = 1;
27421         this.rotate = 0;
27422         this.baseRotate = 1;
27423         this.dragable = false;
27424         this.pinching = false;
27425         this.mouseX = 0;
27426         this.mouseY = 0;
27427         this.cropData = false;
27428         this.notifyEl.dom.innerHTML = this.emptyText;
27429         
27430         this.selectorEl.dom.value = '';
27431         
27432     },
27433     
27434     resize : function()
27435     {
27436         if(this.fireEvent('resize', this) != false){
27437             this.setThumbBoxPosition();
27438             this.setCanvasPosition();
27439         }
27440     },
27441     
27442     onFooterButtonClick : function(e, el, o, type)
27443     {
27444         switch (type) {
27445             case 'rotate-left' :
27446                 this.onRotateLeft(e);
27447                 break;
27448             case 'rotate-right' :
27449                 this.onRotateRight(e);
27450                 break;
27451             case 'picture' :
27452                 this.beforeSelectFile(e);
27453                 break;
27454             case 'trash' :
27455                 this.trash(e);
27456                 break;
27457             case 'crop' :
27458                 this.crop(e);
27459                 break;
27460             case 'download' :
27461                 this.download(e);
27462                 break;
27463             default :
27464                 break;
27465         }
27466         
27467         this.fireEvent('footerbuttonclick', this, type);
27468     },
27469     
27470     beforeSelectFile : function(e)
27471     {
27472         e.preventDefault();
27473         
27474         if(this.fireEvent('beforeselectfile', this) != false){
27475             this.selectorEl.dom.click();
27476         }
27477     },
27478     
27479     onFileSelected : function(e)
27480     {
27481         e.preventDefault();
27482         
27483         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27484             return;
27485         }
27486         
27487         var file = this.selectorEl.dom.files[0];
27488         
27489         if(this.fireEvent('inspect', this, file) != false){
27490             this.prepare(file);
27491         }
27492         
27493     },
27494     
27495     trash : function(e)
27496     {
27497         this.fireEvent('trash', this);
27498     },
27499     
27500     download : function(e)
27501     {
27502         this.fireEvent('download', this);
27503     },
27504     
27505     loadCanvas : function(src)
27506     {   
27507         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27508             
27509             this.reset();
27510             
27511             this.imageEl = document.createElement('img');
27512             
27513             var _this = this;
27514             
27515             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27516             
27517             this.imageEl.src = src;
27518         }
27519     },
27520     
27521     onLoadCanvas : function()
27522     {   
27523         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27524         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27525         
27526         this.bodyEl.un('click', this.beforeSelectFile, this);
27527         
27528         this.notifyEl.hide();
27529         this.thumbEl.show();
27530         this.footerEl.show();
27531         
27532         this.baseRotateLevel();
27533         
27534         if(this.isDocument){
27535             this.setThumbBoxSize();
27536         }
27537         
27538         this.setThumbBoxPosition();
27539         
27540         this.baseScaleLevel();
27541         
27542         this.draw();
27543         
27544         this.resize();
27545         
27546         this.canvasLoaded = true;
27547         
27548         if(this.loadMask){
27549             this.maskEl.unmask();
27550         }
27551         
27552     },
27553     
27554     setCanvasPosition : function()
27555     {   
27556         if(!this.canvasEl){
27557             return;
27558         }
27559         
27560         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27561         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27562         
27563         this.previewEl.setLeft(pw);
27564         this.previewEl.setTop(ph);
27565         
27566     },
27567     
27568     onMouseDown : function(e)
27569     {   
27570         e.stopEvent();
27571         
27572         this.dragable = true;
27573         this.pinching = false;
27574         
27575         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27576             this.dragable = false;
27577             return;
27578         }
27579         
27580         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27581         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27582         
27583     },
27584     
27585     onMouseMove : function(e)
27586     {   
27587         e.stopEvent();
27588         
27589         if(!this.canvasLoaded){
27590             return;
27591         }
27592         
27593         if (!this.dragable){
27594             return;
27595         }
27596         
27597         var minX = Math.ceil(this.thumbEl.getLeft(true));
27598         var minY = Math.ceil(this.thumbEl.getTop(true));
27599         
27600         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27601         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27602         
27603         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27604         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27605         
27606         x = x - this.mouseX;
27607         y = y - this.mouseY;
27608         
27609         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27610         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27611         
27612         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27613         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27614         
27615         this.previewEl.setLeft(bgX);
27616         this.previewEl.setTop(bgY);
27617         
27618         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27619         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27620     },
27621     
27622     onMouseUp : function(e)
27623     {   
27624         e.stopEvent();
27625         
27626         this.dragable = false;
27627     },
27628     
27629     onMouseWheel : function(e)
27630     {   
27631         e.stopEvent();
27632         
27633         this.startScale = this.scale;
27634         
27635         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27636         
27637         if(!this.zoomable()){
27638             this.scale = this.startScale;
27639             return;
27640         }
27641         
27642         this.draw();
27643         
27644         return;
27645     },
27646     
27647     zoomable : function()
27648     {
27649         var minScale = this.thumbEl.getWidth() / this.minWidth;
27650         
27651         if(this.minWidth < this.minHeight){
27652             minScale = this.thumbEl.getHeight() / this.minHeight;
27653         }
27654         
27655         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27656         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27657         
27658         if(
27659                 this.isDocument &&
27660                 (this.rotate == 0 || this.rotate == 180) && 
27661                 (
27662                     width > this.imageEl.OriginWidth || 
27663                     height > this.imageEl.OriginHeight ||
27664                     (width < this.minWidth && height < this.minHeight)
27665                 )
27666         ){
27667             return false;
27668         }
27669         
27670         if(
27671                 this.isDocument &&
27672                 (this.rotate == 90 || this.rotate == 270) && 
27673                 (
27674                     width > this.imageEl.OriginWidth || 
27675                     height > this.imageEl.OriginHeight ||
27676                     (width < this.minHeight && height < this.minWidth)
27677                 )
27678         ){
27679             return false;
27680         }
27681         
27682         if(
27683                 !this.isDocument &&
27684                 (this.rotate == 0 || this.rotate == 180) && 
27685                 (
27686                     width < this.minWidth || 
27687                     width > this.imageEl.OriginWidth || 
27688                     height < this.minHeight || 
27689                     height > this.imageEl.OriginHeight
27690                 )
27691         ){
27692             return false;
27693         }
27694         
27695         if(
27696                 !this.isDocument &&
27697                 (this.rotate == 90 || this.rotate == 270) && 
27698                 (
27699                     width < this.minHeight || 
27700                     width > this.imageEl.OriginWidth || 
27701                     height < this.minWidth || 
27702                     height > this.imageEl.OriginHeight
27703                 )
27704         ){
27705             return false;
27706         }
27707         
27708         return true;
27709         
27710     },
27711     
27712     onRotateLeft : function(e)
27713     {   
27714         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27715             
27716             var minScale = this.thumbEl.getWidth() / this.minWidth;
27717             
27718             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27719             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27720             
27721             this.startScale = this.scale;
27722             
27723             while (this.getScaleLevel() < minScale){
27724             
27725                 this.scale = this.scale + 1;
27726                 
27727                 if(!this.zoomable()){
27728                     break;
27729                 }
27730                 
27731                 if(
27732                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27733                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27734                 ){
27735                     continue;
27736                 }
27737                 
27738                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27739
27740                 this.draw();
27741                 
27742                 return;
27743             }
27744             
27745             this.scale = this.startScale;
27746             
27747             this.onRotateFail();
27748             
27749             return false;
27750         }
27751         
27752         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27753
27754         if(this.isDocument){
27755             this.setThumbBoxSize();
27756             this.setThumbBoxPosition();
27757             this.setCanvasPosition();
27758         }
27759         
27760         this.draw();
27761         
27762         this.fireEvent('rotate', this, 'left');
27763         
27764     },
27765     
27766     onRotateRight : function(e)
27767     {
27768         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27769             
27770             var minScale = this.thumbEl.getWidth() / this.minWidth;
27771         
27772             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27773             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27774             
27775             this.startScale = this.scale;
27776             
27777             while (this.getScaleLevel() < minScale){
27778             
27779                 this.scale = this.scale + 1;
27780                 
27781                 if(!this.zoomable()){
27782                     break;
27783                 }
27784                 
27785                 if(
27786                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27787                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27788                 ){
27789                     continue;
27790                 }
27791                 
27792                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27793
27794                 this.draw();
27795                 
27796                 return;
27797             }
27798             
27799             this.scale = this.startScale;
27800             
27801             this.onRotateFail();
27802             
27803             return false;
27804         }
27805         
27806         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27807
27808         if(this.isDocument){
27809             this.setThumbBoxSize();
27810             this.setThumbBoxPosition();
27811             this.setCanvasPosition();
27812         }
27813         
27814         this.draw();
27815         
27816         this.fireEvent('rotate', this, 'right');
27817     },
27818     
27819     onRotateFail : function()
27820     {
27821         this.errorEl.show(true);
27822         
27823         var _this = this;
27824         
27825         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27826     },
27827     
27828     draw : function()
27829     {
27830         this.previewEl.dom.innerHTML = '';
27831         
27832         var canvasEl = document.createElement("canvas");
27833         
27834         var contextEl = canvasEl.getContext("2d");
27835         
27836         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27837         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27838         var center = this.imageEl.OriginWidth / 2;
27839         
27840         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27841             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27842             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27843             center = this.imageEl.OriginHeight / 2;
27844         }
27845         
27846         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27847         
27848         contextEl.translate(center, center);
27849         contextEl.rotate(this.rotate * Math.PI / 180);
27850
27851         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27852         
27853         this.canvasEl = document.createElement("canvas");
27854         
27855         this.contextEl = this.canvasEl.getContext("2d");
27856         
27857         switch (this.rotate) {
27858             case 0 :
27859                 
27860                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27861                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27862                 
27863                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27864                 
27865                 break;
27866             case 90 : 
27867                 
27868                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27869                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27870                 
27871                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27872                     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);
27873                     break;
27874                 }
27875                 
27876                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27877                 
27878                 break;
27879             case 180 :
27880                 
27881                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27882                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27883                 
27884                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27885                     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);
27886                     break;
27887                 }
27888                 
27889                 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);
27890                 
27891                 break;
27892             case 270 :
27893                 
27894                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27895                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27896         
27897                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27898                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27899                     break;
27900                 }
27901                 
27902                 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);
27903                 
27904                 break;
27905             default : 
27906                 break;
27907         }
27908         
27909         this.previewEl.appendChild(this.canvasEl);
27910         
27911         this.setCanvasPosition();
27912     },
27913     
27914     crop : function()
27915     {
27916         if(!this.canvasLoaded){
27917             return;
27918         }
27919         
27920         var imageCanvas = document.createElement("canvas");
27921         
27922         var imageContext = imageCanvas.getContext("2d");
27923         
27924         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27925         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27926         
27927         var center = imageCanvas.width / 2;
27928         
27929         imageContext.translate(center, center);
27930         
27931         imageContext.rotate(this.rotate * Math.PI / 180);
27932         
27933         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27934         
27935         var canvas = document.createElement("canvas");
27936         
27937         var context = canvas.getContext("2d");
27938                 
27939         canvas.width = this.minWidth;
27940         canvas.height = this.minHeight;
27941
27942         switch (this.rotate) {
27943             case 0 :
27944                 
27945                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27946                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27947                 
27948                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27949                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27950                 
27951                 var targetWidth = this.minWidth - 2 * x;
27952                 var targetHeight = this.minHeight - 2 * y;
27953                 
27954                 var scale = 1;
27955                 
27956                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27957                     scale = targetWidth / width;
27958                 }
27959                 
27960                 if(x > 0 && y == 0){
27961                     scale = targetHeight / height;
27962                 }
27963                 
27964                 if(x > 0 && y > 0){
27965                     scale = targetWidth / width;
27966                     
27967                     if(width < height){
27968                         scale = targetHeight / height;
27969                     }
27970                 }
27971                 
27972                 context.scale(scale, scale);
27973                 
27974                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27975                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27976
27977                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27978                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27979
27980                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27981                 
27982                 break;
27983             case 90 : 
27984                 
27985                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27986                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27987                 
27988                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27989                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27990                 
27991                 var targetWidth = this.minWidth - 2 * x;
27992                 var targetHeight = this.minHeight - 2 * y;
27993                 
27994                 var scale = 1;
27995                 
27996                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27997                     scale = targetWidth / width;
27998                 }
27999                 
28000                 if(x > 0 && y == 0){
28001                     scale = targetHeight / height;
28002                 }
28003                 
28004                 if(x > 0 && y > 0){
28005                     scale = targetWidth / width;
28006                     
28007                     if(width < height){
28008                         scale = targetHeight / height;
28009                     }
28010                 }
28011                 
28012                 context.scale(scale, scale);
28013                 
28014                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28015                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28016
28017                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28018                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28019                 
28020                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28021                 
28022                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28023                 
28024                 break;
28025             case 180 :
28026                 
28027                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28028                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28029                 
28030                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28031                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28032                 
28033                 var targetWidth = this.minWidth - 2 * x;
28034                 var targetHeight = this.minHeight - 2 * y;
28035                 
28036                 var scale = 1;
28037                 
28038                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28039                     scale = targetWidth / width;
28040                 }
28041                 
28042                 if(x > 0 && y == 0){
28043                     scale = targetHeight / height;
28044                 }
28045                 
28046                 if(x > 0 && y > 0){
28047                     scale = targetWidth / width;
28048                     
28049                     if(width < height){
28050                         scale = targetHeight / height;
28051                     }
28052                 }
28053                 
28054                 context.scale(scale, scale);
28055                 
28056                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28057                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28058
28059                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28060                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28061
28062                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28063                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28064                 
28065                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28066                 
28067                 break;
28068             case 270 :
28069                 
28070                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28071                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28072                 
28073                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28074                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28075                 
28076                 var targetWidth = this.minWidth - 2 * x;
28077                 var targetHeight = this.minHeight - 2 * y;
28078                 
28079                 var scale = 1;
28080                 
28081                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28082                     scale = targetWidth / width;
28083                 }
28084                 
28085                 if(x > 0 && y == 0){
28086                     scale = targetHeight / height;
28087                 }
28088                 
28089                 if(x > 0 && y > 0){
28090                     scale = targetWidth / width;
28091                     
28092                     if(width < height){
28093                         scale = targetHeight / height;
28094                     }
28095                 }
28096                 
28097                 context.scale(scale, scale);
28098                 
28099                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28100                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28101
28102                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28103                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28104                 
28105                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28106                 
28107                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28108                 
28109                 break;
28110             default : 
28111                 break;
28112         }
28113         
28114         this.cropData = canvas.toDataURL(this.cropType);
28115         
28116         if(this.fireEvent('crop', this, this.cropData) !== false){
28117             this.process(this.file, this.cropData);
28118         }
28119         
28120         return;
28121         
28122     },
28123     
28124     setThumbBoxSize : function()
28125     {
28126         var width, height;
28127         
28128         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28129             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28130             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28131             
28132             this.minWidth = width;
28133             this.minHeight = height;
28134             
28135             if(this.rotate == 90 || this.rotate == 270){
28136                 this.minWidth = height;
28137                 this.minHeight = width;
28138             }
28139         }
28140         
28141         height = 300;
28142         width = Math.ceil(this.minWidth * height / this.minHeight);
28143         
28144         if(this.minWidth > this.minHeight){
28145             width = 300;
28146             height = Math.ceil(this.minHeight * width / this.minWidth);
28147         }
28148         
28149         this.thumbEl.setStyle({
28150             width : width + 'px',
28151             height : height + 'px'
28152         });
28153
28154         return;
28155             
28156     },
28157     
28158     setThumbBoxPosition : function()
28159     {
28160         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28161         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28162         
28163         this.thumbEl.setLeft(x);
28164         this.thumbEl.setTop(y);
28165         
28166     },
28167     
28168     baseRotateLevel : function()
28169     {
28170         this.baseRotate = 1;
28171         
28172         if(
28173                 typeof(this.exif) != 'undefined' &&
28174                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28175                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28176         ){
28177             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28178         }
28179         
28180         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28181         
28182     },
28183     
28184     baseScaleLevel : function()
28185     {
28186         var width, height;
28187         
28188         if(this.isDocument){
28189             
28190             if(this.baseRotate == 6 || this.baseRotate == 8){
28191             
28192                 height = this.thumbEl.getHeight();
28193                 this.baseScale = height / this.imageEl.OriginWidth;
28194
28195                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28196                     width = this.thumbEl.getWidth();
28197                     this.baseScale = width / this.imageEl.OriginHeight;
28198                 }
28199
28200                 return;
28201             }
28202
28203             height = this.thumbEl.getHeight();
28204             this.baseScale = height / this.imageEl.OriginHeight;
28205
28206             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28207                 width = this.thumbEl.getWidth();
28208                 this.baseScale = width / this.imageEl.OriginWidth;
28209             }
28210
28211             return;
28212         }
28213         
28214         if(this.baseRotate == 6 || this.baseRotate == 8){
28215             
28216             width = this.thumbEl.getHeight();
28217             this.baseScale = width / this.imageEl.OriginHeight;
28218             
28219             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28220                 height = this.thumbEl.getWidth();
28221                 this.baseScale = height / this.imageEl.OriginHeight;
28222             }
28223             
28224             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28225                 height = this.thumbEl.getWidth();
28226                 this.baseScale = height / this.imageEl.OriginHeight;
28227                 
28228                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28229                     width = this.thumbEl.getHeight();
28230                     this.baseScale = width / this.imageEl.OriginWidth;
28231                 }
28232             }
28233             
28234             return;
28235         }
28236         
28237         width = this.thumbEl.getWidth();
28238         this.baseScale = width / this.imageEl.OriginWidth;
28239         
28240         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28241             height = this.thumbEl.getHeight();
28242             this.baseScale = height / this.imageEl.OriginHeight;
28243         }
28244         
28245         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28246             
28247             height = this.thumbEl.getHeight();
28248             this.baseScale = height / this.imageEl.OriginHeight;
28249             
28250             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28251                 width = this.thumbEl.getWidth();
28252                 this.baseScale = width / this.imageEl.OriginWidth;
28253             }
28254             
28255         }
28256         
28257         return;
28258     },
28259     
28260     getScaleLevel : function()
28261     {
28262         return this.baseScale * Math.pow(1.1, this.scale);
28263     },
28264     
28265     onTouchStart : function(e)
28266     {
28267         if(!this.canvasLoaded){
28268             this.beforeSelectFile(e);
28269             return;
28270         }
28271         
28272         var touches = e.browserEvent.touches;
28273         
28274         if(!touches){
28275             return;
28276         }
28277         
28278         if(touches.length == 1){
28279             this.onMouseDown(e);
28280             return;
28281         }
28282         
28283         if(touches.length != 2){
28284             return;
28285         }
28286         
28287         var coords = [];
28288         
28289         for(var i = 0, finger; finger = touches[i]; i++){
28290             coords.push(finger.pageX, finger.pageY);
28291         }
28292         
28293         var x = Math.pow(coords[0] - coords[2], 2);
28294         var y = Math.pow(coords[1] - coords[3], 2);
28295         
28296         this.startDistance = Math.sqrt(x + y);
28297         
28298         this.startScale = this.scale;
28299         
28300         this.pinching = true;
28301         this.dragable = false;
28302         
28303     },
28304     
28305     onTouchMove : function(e)
28306     {
28307         if(!this.pinching && !this.dragable){
28308             return;
28309         }
28310         
28311         var touches = e.browserEvent.touches;
28312         
28313         if(!touches){
28314             return;
28315         }
28316         
28317         if(this.dragable){
28318             this.onMouseMove(e);
28319             return;
28320         }
28321         
28322         var coords = [];
28323         
28324         for(var i = 0, finger; finger = touches[i]; i++){
28325             coords.push(finger.pageX, finger.pageY);
28326         }
28327         
28328         var x = Math.pow(coords[0] - coords[2], 2);
28329         var y = Math.pow(coords[1] - coords[3], 2);
28330         
28331         this.endDistance = Math.sqrt(x + y);
28332         
28333         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28334         
28335         if(!this.zoomable()){
28336             this.scale = this.startScale;
28337             return;
28338         }
28339         
28340         this.draw();
28341         
28342     },
28343     
28344     onTouchEnd : function(e)
28345     {
28346         this.pinching = false;
28347         this.dragable = false;
28348         
28349     },
28350     
28351     process : function(file, crop)
28352     {
28353         if(this.loadMask){
28354             this.maskEl.mask(this.loadingText);
28355         }
28356         
28357         this.xhr = new XMLHttpRequest();
28358         
28359         file.xhr = this.xhr;
28360
28361         this.xhr.open(this.method, this.url, true);
28362         
28363         var headers = {
28364             "Accept": "application/json",
28365             "Cache-Control": "no-cache",
28366             "X-Requested-With": "XMLHttpRequest"
28367         };
28368         
28369         for (var headerName in headers) {
28370             var headerValue = headers[headerName];
28371             if (headerValue) {
28372                 this.xhr.setRequestHeader(headerName, headerValue);
28373             }
28374         }
28375         
28376         var _this = this;
28377         
28378         this.xhr.onload = function()
28379         {
28380             _this.xhrOnLoad(_this.xhr);
28381         }
28382         
28383         this.xhr.onerror = function()
28384         {
28385             _this.xhrOnError(_this.xhr);
28386         }
28387         
28388         var formData = new FormData();
28389
28390         formData.append('returnHTML', 'NO');
28391         
28392         if(crop){
28393             formData.append('crop', crop);
28394         }
28395         
28396         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28397             formData.append(this.paramName, file, file.name);
28398         }
28399         
28400         if(typeof(file.filename) != 'undefined'){
28401             formData.append('filename', file.filename);
28402         }
28403         
28404         if(typeof(file.mimetype) != 'undefined'){
28405             formData.append('mimetype', file.mimetype);
28406         }
28407         
28408         if(this.fireEvent('arrange', this, formData) != false){
28409             this.xhr.send(formData);
28410         };
28411     },
28412     
28413     xhrOnLoad : function(xhr)
28414     {
28415         if(this.loadMask){
28416             this.maskEl.unmask();
28417         }
28418         
28419         if (xhr.readyState !== 4) {
28420             this.fireEvent('exception', this, xhr);
28421             return;
28422         }
28423
28424         var response = Roo.decode(xhr.responseText);
28425         
28426         if(!response.success){
28427             this.fireEvent('exception', this, xhr);
28428             return;
28429         }
28430         
28431         var response = Roo.decode(xhr.responseText);
28432         
28433         this.fireEvent('upload', this, response);
28434         
28435     },
28436     
28437     xhrOnError : function()
28438     {
28439         if(this.loadMask){
28440             this.maskEl.unmask();
28441         }
28442         
28443         Roo.log('xhr on error');
28444         
28445         var response = Roo.decode(xhr.responseText);
28446           
28447         Roo.log(response);
28448         
28449     },
28450     
28451     prepare : function(file)
28452     {   
28453         if(this.loadMask){
28454             this.maskEl.mask(this.loadingText);
28455         }
28456         
28457         this.file = false;
28458         this.exif = {};
28459         
28460         if(typeof(file) === 'string'){
28461             this.loadCanvas(file);
28462             return;
28463         }
28464         
28465         if(!file || !this.urlAPI){
28466             return;
28467         }
28468         
28469         this.file = file;
28470         this.cropType = file.type;
28471         
28472         var _this = this;
28473         
28474         if(this.fireEvent('prepare', this, this.file) != false){
28475             
28476             var reader = new FileReader();
28477             
28478             reader.onload = function (e) {
28479                 if (e.target.error) {
28480                     Roo.log(e.target.error);
28481                     return;
28482                 }
28483                 
28484                 var buffer = e.target.result,
28485                     dataView = new DataView(buffer),
28486                     offset = 2,
28487                     maxOffset = dataView.byteLength - 4,
28488                     markerBytes,
28489                     markerLength;
28490                 
28491                 if (dataView.getUint16(0) === 0xffd8) {
28492                     while (offset < maxOffset) {
28493                         markerBytes = dataView.getUint16(offset);
28494                         
28495                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28496                             markerLength = dataView.getUint16(offset + 2) + 2;
28497                             if (offset + markerLength > dataView.byteLength) {
28498                                 Roo.log('Invalid meta data: Invalid segment size.');
28499                                 break;
28500                             }
28501                             
28502                             if(markerBytes == 0xffe1){
28503                                 _this.parseExifData(
28504                                     dataView,
28505                                     offset,
28506                                     markerLength
28507                                 );
28508                             }
28509                             
28510                             offset += markerLength;
28511                             
28512                             continue;
28513                         }
28514                         
28515                         break;
28516                     }
28517                     
28518                 }
28519                 
28520                 var url = _this.urlAPI.createObjectURL(_this.file);
28521                 
28522                 _this.loadCanvas(url);
28523                 
28524                 return;
28525             }
28526             
28527             reader.readAsArrayBuffer(this.file);
28528             
28529         }
28530         
28531     },
28532     
28533     parseExifData : function(dataView, offset, length)
28534     {
28535         var tiffOffset = offset + 10,
28536             littleEndian,
28537             dirOffset;
28538     
28539         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28540             // No Exif data, might be XMP data instead
28541             return;
28542         }
28543         
28544         // Check for the ASCII code for "Exif" (0x45786966):
28545         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28546             // No Exif data, might be XMP data instead
28547             return;
28548         }
28549         if (tiffOffset + 8 > dataView.byteLength) {
28550             Roo.log('Invalid Exif data: Invalid segment size.');
28551             return;
28552         }
28553         // Check for the two null bytes:
28554         if (dataView.getUint16(offset + 8) !== 0x0000) {
28555             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28556             return;
28557         }
28558         // Check the byte alignment:
28559         switch (dataView.getUint16(tiffOffset)) {
28560         case 0x4949:
28561             littleEndian = true;
28562             break;
28563         case 0x4D4D:
28564             littleEndian = false;
28565             break;
28566         default:
28567             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28568             return;
28569         }
28570         // Check for the TIFF tag marker (0x002A):
28571         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28572             Roo.log('Invalid Exif data: Missing TIFF marker.');
28573             return;
28574         }
28575         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28576         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28577         
28578         this.parseExifTags(
28579             dataView,
28580             tiffOffset,
28581             tiffOffset + dirOffset,
28582             littleEndian
28583         );
28584     },
28585     
28586     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28587     {
28588         var tagsNumber,
28589             dirEndOffset,
28590             i;
28591         if (dirOffset + 6 > dataView.byteLength) {
28592             Roo.log('Invalid Exif data: Invalid directory offset.');
28593             return;
28594         }
28595         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28596         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28597         if (dirEndOffset + 4 > dataView.byteLength) {
28598             Roo.log('Invalid Exif data: Invalid directory size.');
28599             return;
28600         }
28601         for (i = 0; i < tagsNumber; i += 1) {
28602             this.parseExifTag(
28603                 dataView,
28604                 tiffOffset,
28605                 dirOffset + 2 + 12 * i, // tag offset
28606                 littleEndian
28607             );
28608         }
28609         // Return the offset to the next directory:
28610         return dataView.getUint32(dirEndOffset, littleEndian);
28611     },
28612     
28613     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28614     {
28615         var tag = dataView.getUint16(offset, littleEndian);
28616         
28617         this.exif[tag] = this.getExifValue(
28618             dataView,
28619             tiffOffset,
28620             offset,
28621             dataView.getUint16(offset + 2, littleEndian), // tag type
28622             dataView.getUint32(offset + 4, littleEndian), // tag length
28623             littleEndian
28624         );
28625     },
28626     
28627     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28628     {
28629         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28630             tagSize,
28631             dataOffset,
28632             values,
28633             i,
28634             str,
28635             c;
28636     
28637         if (!tagType) {
28638             Roo.log('Invalid Exif data: Invalid tag type.');
28639             return;
28640         }
28641         
28642         tagSize = tagType.size * length;
28643         // Determine if the value is contained in the dataOffset bytes,
28644         // or if the value at the dataOffset is a pointer to the actual data:
28645         dataOffset = tagSize > 4 ?
28646                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28647         if (dataOffset + tagSize > dataView.byteLength) {
28648             Roo.log('Invalid Exif data: Invalid data offset.');
28649             return;
28650         }
28651         if (length === 1) {
28652             return tagType.getValue(dataView, dataOffset, littleEndian);
28653         }
28654         values = [];
28655         for (i = 0; i < length; i += 1) {
28656             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28657         }
28658         
28659         if (tagType.ascii) {
28660             str = '';
28661             // Concatenate the chars:
28662             for (i = 0; i < values.length; i += 1) {
28663                 c = values[i];
28664                 // Ignore the terminating NULL byte(s):
28665                 if (c === '\u0000') {
28666                     break;
28667                 }
28668                 str += c;
28669             }
28670             return str;
28671         }
28672         return values;
28673     }
28674     
28675 });
28676
28677 Roo.apply(Roo.bootstrap.UploadCropbox, {
28678     tags : {
28679         'Orientation': 0x0112
28680     },
28681     
28682     Orientation: {
28683             1: 0, //'top-left',
28684 //            2: 'top-right',
28685             3: 180, //'bottom-right',
28686 //            4: 'bottom-left',
28687 //            5: 'left-top',
28688             6: 90, //'right-top',
28689 //            7: 'right-bottom',
28690             8: 270 //'left-bottom'
28691     },
28692     
28693     exifTagTypes : {
28694         // byte, 8-bit unsigned int:
28695         1: {
28696             getValue: function (dataView, dataOffset) {
28697                 return dataView.getUint8(dataOffset);
28698             },
28699             size: 1
28700         },
28701         // ascii, 8-bit byte:
28702         2: {
28703             getValue: function (dataView, dataOffset) {
28704                 return String.fromCharCode(dataView.getUint8(dataOffset));
28705             },
28706             size: 1,
28707             ascii: true
28708         },
28709         // short, 16 bit int:
28710         3: {
28711             getValue: function (dataView, dataOffset, littleEndian) {
28712                 return dataView.getUint16(dataOffset, littleEndian);
28713             },
28714             size: 2
28715         },
28716         // long, 32 bit int:
28717         4: {
28718             getValue: function (dataView, dataOffset, littleEndian) {
28719                 return dataView.getUint32(dataOffset, littleEndian);
28720             },
28721             size: 4
28722         },
28723         // rational = two long values, first is numerator, second is denominator:
28724         5: {
28725             getValue: function (dataView, dataOffset, littleEndian) {
28726                 return dataView.getUint32(dataOffset, littleEndian) /
28727                     dataView.getUint32(dataOffset + 4, littleEndian);
28728             },
28729             size: 8
28730         },
28731         // slong, 32 bit signed int:
28732         9: {
28733             getValue: function (dataView, dataOffset, littleEndian) {
28734                 return dataView.getInt32(dataOffset, littleEndian);
28735             },
28736             size: 4
28737         },
28738         // srational, two slongs, first is numerator, second is denominator:
28739         10: {
28740             getValue: function (dataView, dataOffset, littleEndian) {
28741                 return dataView.getInt32(dataOffset, littleEndian) /
28742                     dataView.getInt32(dataOffset + 4, littleEndian);
28743             },
28744             size: 8
28745         }
28746     },
28747     
28748     footer : {
28749         STANDARD : [
28750             {
28751                 tag : 'div',
28752                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28753                 action : 'rotate-left',
28754                 cn : [
28755                     {
28756                         tag : 'button',
28757                         cls : 'btn btn-default',
28758                         html : '<i class="fa fa-undo"></i>'
28759                     }
28760                 ]
28761             },
28762             {
28763                 tag : 'div',
28764                 cls : 'btn-group roo-upload-cropbox-picture',
28765                 action : 'picture',
28766                 cn : [
28767                     {
28768                         tag : 'button',
28769                         cls : 'btn btn-default',
28770                         html : '<i class="fa fa-picture-o"></i>'
28771                     }
28772                 ]
28773             },
28774             {
28775                 tag : 'div',
28776                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28777                 action : 'rotate-right',
28778                 cn : [
28779                     {
28780                         tag : 'button',
28781                         cls : 'btn btn-default',
28782                         html : '<i class="fa fa-repeat"></i>'
28783                     }
28784                 ]
28785             }
28786         ],
28787         DOCUMENT : [
28788             {
28789                 tag : 'div',
28790                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28791                 action : 'rotate-left',
28792                 cn : [
28793                     {
28794                         tag : 'button',
28795                         cls : 'btn btn-default',
28796                         html : '<i class="fa fa-undo"></i>'
28797                     }
28798                 ]
28799             },
28800             {
28801                 tag : 'div',
28802                 cls : 'btn-group roo-upload-cropbox-download',
28803                 action : 'download',
28804                 cn : [
28805                     {
28806                         tag : 'button',
28807                         cls : 'btn btn-default',
28808                         html : '<i class="fa fa-download"></i>'
28809                     }
28810                 ]
28811             },
28812             {
28813                 tag : 'div',
28814                 cls : 'btn-group roo-upload-cropbox-crop',
28815                 action : 'crop',
28816                 cn : [
28817                     {
28818                         tag : 'button',
28819                         cls : 'btn btn-default',
28820                         html : '<i class="fa fa-crop"></i>'
28821                     }
28822                 ]
28823             },
28824             {
28825                 tag : 'div',
28826                 cls : 'btn-group roo-upload-cropbox-trash',
28827                 action : 'trash',
28828                 cn : [
28829                     {
28830                         tag : 'button',
28831                         cls : 'btn btn-default',
28832                         html : '<i class="fa fa-trash"></i>'
28833                     }
28834                 ]
28835             },
28836             {
28837                 tag : 'div',
28838                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28839                 action : 'rotate-right',
28840                 cn : [
28841                     {
28842                         tag : 'button',
28843                         cls : 'btn btn-default',
28844                         html : '<i class="fa fa-repeat"></i>'
28845                     }
28846                 ]
28847             }
28848         ],
28849         ROTATOR : [
28850             {
28851                 tag : 'div',
28852                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28853                 action : 'rotate-left',
28854                 cn : [
28855                     {
28856                         tag : 'button',
28857                         cls : 'btn btn-default',
28858                         html : '<i class="fa fa-undo"></i>'
28859                     }
28860                 ]
28861             },
28862             {
28863                 tag : 'div',
28864                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28865                 action : 'rotate-right',
28866                 cn : [
28867                     {
28868                         tag : 'button',
28869                         cls : 'btn btn-default',
28870                         html : '<i class="fa fa-repeat"></i>'
28871                     }
28872                 ]
28873             }
28874         ]
28875     }
28876 });
28877
28878 /*
28879 * Licence: LGPL
28880 */
28881
28882 /**
28883  * @class Roo.bootstrap.DocumentManager
28884  * @extends Roo.bootstrap.Component
28885  * Bootstrap DocumentManager class
28886  * @cfg {String} paramName default 'imageUpload'
28887  * @cfg {String} toolTipName default 'filename'
28888  * @cfg {String} method default POST
28889  * @cfg {String} url action url
28890  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28891  * @cfg {Boolean} multiple multiple upload default true
28892  * @cfg {Number} thumbSize default 300
28893  * @cfg {String} fieldLabel
28894  * @cfg {Number} labelWidth default 4
28895  * @cfg {String} labelAlign (left|top) default left
28896  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28897 * @cfg {Number} labellg set the width of label (1-12)
28898  * @cfg {Number} labelmd set the width of label (1-12)
28899  * @cfg {Number} labelsm set the width of label (1-12)
28900  * @cfg {Number} labelxs set the width of label (1-12)
28901  * 
28902  * @constructor
28903  * Create a new DocumentManager
28904  * @param {Object} config The config object
28905  */
28906
28907 Roo.bootstrap.DocumentManager = function(config){
28908     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28909     
28910     this.files = [];
28911     this.delegates = [];
28912     
28913     this.addEvents({
28914         /**
28915          * @event initial
28916          * Fire when initial the DocumentManager
28917          * @param {Roo.bootstrap.DocumentManager} this
28918          */
28919         "initial" : true,
28920         /**
28921          * @event inspect
28922          * inspect selected file
28923          * @param {Roo.bootstrap.DocumentManager} this
28924          * @param {File} file
28925          */
28926         "inspect" : true,
28927         /**
28928          * @event exception
28929          * Fire when xhr load exception
28930          * @param {Roo.bootstrap.DocumentManager} this
28931          * @param {XMLHttpRequest} xhr
28932          */
28933         "exception" : true,
28934         /**
28935          * @event afterupload
28936          * Fire when xhr load exception
28937          * @param {Roo.bootstrap.DocumentManager} this
28938          * @param {XMLHttpRequest} xhr
28939          */
28940         "afterupload" : true,
28941         /**
28942          * @event prepare
28943          * prepare the form data
28944          * @param {Roo.bootstrap.DocumentManager} this
28945          * @param {Object} formData
28946          */
28947         "prepare" : true,
28948         /**
28949          * @event remove
28950          * Fire when remove the file
28951          * @param {Roo.bootstrap.DocumentManager} this
28952          * @param {Object} file
28953          */
28954         "remove" : true,
28955         /**
28956          * @event refresh
28957          * Fire after refresh the file
28958          * @param {Roo.bootstrap.DocumentManager} this
28959          */
28960         "refresh" : true,
28961         /**
28962          * @event click
28963          * Fire after click the image
28964          * @param {Roo.bootstrap.DocumentManager} this
28965          * @param {Object} file
28966          */
28967         "click" : true,
28968         /**
28969          * @event edit
28970          * Fire when upload a image and editable set to true
28971          * @param {Roo.bootstrap.DocumentManager} this
28972          * @param {Object} file
28973          */
28974         "edit" : true,
28975         /**
28976          * @event beforeselectfile
28977          * Fire before select file
28978          * @param {Roo.bootstrap.DocumentManager} this
28979          */
28980         "beforeselectfile" : true,
28981         /**
28982          * @event process
28983          * Fire before process file
28984          * @param {Roo.bootstrap.DocumentManager} this
28985          * @param {Object} file
28986          */
28987         "process" : true,
28988         /**
28989          * @event previewrendered
28990          * Fire when preview rendered
28991          * @param {Roo.bootstrap.DocumentManager} this
28992          * @param {Object} file
28993          */
28994         "previewrendered" : true,
28995         /**
28996          */
28997         "previewResize" : true
28998         
28999     });
29000 };
29001
29002 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29003     
29004     boxes : 0,
29005     inputName : '',
29006     thumbSize : 300,
29007     multiple : true,
29008     files : false,
29009     method : 'POST',
29010     url : '',
29011     paramName : 'imageUpload',
29012     toolTipName : 'filename',
29013     fieldLabel : '',
29014     labelWidth : 4,
29015     labelAlign : 'left',
29016     editable : true,
29017     delegates : false,
29018     xhr : false, 
29019     
29020     labellg : 0,
29021     labelmd : 0,
29022     labelsm : 0,
29023     labelxs : 0,
29024     
29025     getAutoCreate : function()
29026     {   
29027         var managerWidget = {
29028             tag : 'div',
29029             cls : 'roo-document-manager',
29030             cn : [
29031                 {
29032                     tag : 'input',
29033                     cls : 'roo-document-manager-selector',
29034                     type : 'file'
29035                 },
29036                 {
29037                     tag : 'div',
29038                     cls : 'roo-document-manager-uploader',
29039                     cn : [
29040                         {
29041                             tag : 'div',
29042                             cls : 'roo-document-manager-upload-btn',
29043                             html : '<i class="fa fa-plus"></i>'
29044                         }
29045                     ]
29046                     
29047                 }
29048             ]
29049         };
29050         
29051         var content = [
29052             {
29053                 tag : 'div',
29054                 cls : 'column col-md-12',
29055                 cn : managerWidget
29056             }
29057         ];
29058         
29059         if(this.fieldLabel.length){
29060             
29061             content = [
29062                 {
29063                     tag : 'div',
29064                     cls : 'column col-md-12',
29065                     html : this.fieldLabel
29066                 },
29067                 {
29068                     tag : 'div',
29069                     cls : 'column col-md-12',
29070                     cn : managerWidget
29071                 }
29072             ];
29073
29074             if(this.labelAlign == 'left'){
29075                 content = [
29076                     {
29077                         tag : 'div',
29078                         cls : 'column',
29079                         html : this.fieldLabel
29080                     },
29081                     {
29082                         tag : 'div',
29083                         cls : 'column',
29084                         cn : managerWidget
29085                     }
29086                 ];
29087                 
29088                 if(this.labelWidth > 12){
29089                     content[0].style = "width: " + this.labelWidth + 'px';
29090                 }
29091
29092                 if(this.labelWidth < 13 && this.labelmd == 0){
29093                     this.labelmd = this.labelWidth;
29094                 }
29095
29096                 if(this.labellg > 0){
29097                     content[0].cls += ' col-lg-' + this.labellg;
29098                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29099                 }
29100
29101                 if(this.labelmd > 0){
29102                     content[0].cls += ' col-md-' + this.labelmd;
29103                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29104                 }
29105
29106                 if(this.labelsm > 0){
29107                     content[0].cls += ' col-sm-' + this.labelsm;
29108                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29109                 }
29110
29111                 if(this.labelxs > 0){
29112                     content[0].cls += ' col-xs-' + this.labelxs;
29113                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29114                 }
29115                 
29116             }
29117         }
29118         
29119         var cfg = {
29120             tag : 'div',
29121             cls : 'row clearfix',
29122             cn : content
29123         };
29124         
29125         return cfg;
29126         
29127     },
29128     
29129     initEvents : function()
29130     {
29131         this.managerEl = this.el.select('.roo-document-manager', true).first();
29132         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29133         
29134         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29135         this.selectorEl.hide();
29136         
29137         if(this.multiple){
29138             this.selectorEl.attr('multiple', 'multiple');
29139         }
29140         
29141         this.selectorEl.on('change', this.onFileSelected, this);
29142         
29143         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29144         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29145         
29146         this.uploader.on('click', this.onUploaderClick, this);
29147         
29148         this.renderProgressDialog();
29149         
29150         var _this = this;
29151         
29152         window.addEventListener("resize", function() { _this.refresh(); } );
29153         
29154         this.fireEvent('initial', this);
29155     },
29156     
29157     renderProgressDialog : function()
29158     {
29159         var _this = this;
29160         
29161         this.progressDialog = new Roo.bootstrap.Modal({
29162             cls : 'roo-document-manager-progress-dialog',
29163             allow_close : false,
29164             title : '',
29165             buttons : [
29166                 {
29167                     name  :'cancel',
29168                     weight : 'danger',
29169                     html : 'Cancel'
29170                 }
29171             ], 
29172             listeners : { 
29173                 btnclick : function() {
29174                     _this.uploadCancel();
29175                     this.hide();
29176                 }
29177             }
29178         });
29179          
29180         this.progressDialog.render(Roo.get(document.body));
29181          
29182         this.progress = new Roo.bootstrap.Progress({
29183             cls : 'roo-document-manager-progress',
29184             active : true,
29185             striped : true
29186         });
29187         
29188         this.progress.render(this.progressDialog.getChildContainer());
29189         
29190         this.progressBar = new Roo.bootstrap.ProgressBar({
29191             cls : 'roo-document-manager-progress-bar',
29192             aria_valuenow : 0,
29193             aria_valuemin : 0,
29194             aria_valuemax : 12,
29195             panel : 'success'
29196         });
29197         
29198         this.progressBar.render(this.progress.getChildContainer());
29199     },
29200     
29201     onUploaderClick : function(e)
29202     {
29203         e.preventDefault();
29204      
29205         if(this.fireEvent('beforeselectfile', this) != false){
29206             this.selectorEl.dom.click();
29207         }
29208         
29209     },
29210     
29211     onFileSelected : function(e)
29212     {
29213         e.preventDefault();
29214         
29215         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29216             return;
29217         }
29218         
29219         Roo.each(this.selectorEl.dom.files, function(file){
29220             if(this.fireEvent('inspect', this, file) != false){
29221                 this.files.push(file);
29222             }
29223         }, this);
29224         
29225         this.queue();
29226         
29227     },
29228     
29229     queue : function()
29230     {
29231         this.selectorEl.dom.value = '';
29232         
29233         if(!this.files || !this.files.length){
29234             return;
29235         }
29236         
29237         if(this.boxes > 0 && this.files.length > this.boxes){
29238             this.files = this.files.slice(0, this.boxes);
29239         }
29240         
29241         this.uploader.show();
29242         
29243         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29244             this.uploader.hide();
29245         }
29246         
29247         var _this = this;
29248         
29249         var files = [];
29250         
29251         var docs = [];
29252         
29253         Roo.each(this.files, function(file){
29254             
29255             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29256                 var f = this.renderPreview(file);
29257                 files.push(f);
29258                 return;
29259             }
29260             
29261             if(file.type.indexOf('image') != -1){
29262                 this.delegates.push(
29263                     (function(){
29264                         _this.process(file);
29265                     }).createDelegate(this)
29266                 );
29267         
29268                 return;
29269             }
29270             
29271             docs.push(
29272                 (function(){
29273                     _this.process(file);
29274                 }).createDelegate(this)
29275             );
29276             
29277         }, this);
29278         
29279         this.files = files;
29280         
29281         this.delegates = this.delegates.concat(docs);
29282         
29283         if(!this.delegates.length){
29284             this.refresh();
29285             return;
29286         }
29287         
29288         this.progressBar.aria_valuemax = this.delegates.length;
29289         
29290         this.arrange();
29291         
29292         return;
29293     },
29294     
29295     arrange : function()
29296     {
29297         if(!this.delegates.length){
29298             this.progressDialog.hide();
29299             this.refresh();
29300             return;
29301         }
29302         
29303         var delegate = this.delegates.shift();
29304         
29305         this.progressDialog.show();
29306         
29307         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29308         
29309         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29310         
29311         delegate();
29312     },
29313     
29314     refresh : function()
29315     {
29316         this.uploader.show();
29317         
29318         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29319             this.uploader.hide();
29320         }
29321         
29322         Roo.isTouch ? this.closable(false) : this.closable(true);
29323         
29324         this.fireEvent('refresh', this);
29325     },
29326     
29327     onRemove : function(e, el, o)
29328     {
29329         e.preventDefault();
29330         
29331         this.fireEvent('remove', this, o);
29332         
29333     },
29334     
29335     remove : function(o)
29336     {
29337         var files = [];
29338         
29339         Roo.each(this.files, function(file){
29340             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29341                 files.push(file);
29342                 return;
29343             }
29344
29345             o.target.remove();
29346
29347         }, this);
29348         
29349         this.files = files;
29350         
29351         this.refresh();
29352     },
29353     
29354     clear : function()
29355     {
29356         Roo.each(this.files, function(file){
29357             if(!file.target){
29358                 return;
29359             }
29360             
29361             file.target.remove();
29362
29363         }, this);
29364         
29365         this.files = [];
29366         
29367         this.refresh();
29368     },
29369     
29370     onClick : function(e, el, o)
29371     {
29372         e.preventDefault();
29373         
29374         this.fireEvent('click', this, o);
29375         
29376     },
29377     
29378     closable : function(closable)
29379     {
29380         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29381             
29382             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29383             
29384             if(closable){
29385                 el.show();
29386                 return;
29387             }
29388             
29389             el.hide();
29390             
29391         }, this);
29392     },
29393     
29394     xhrOnLoad : function(xhr)
29395     {
29396         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29397             el.remove();
29398         }, this);
29399         
29400         if (xhr.readyState !== 4) {
29401             this.arrange();
29402             this.fireEvent('exception', this, xhr);
29403             return;
29404         }
29405
29406         var response = Roo.decode(xhr.responseText);
29407         
29408         if(!response.success){
29409             this.arrange();
29410             this.fireEvent('exception', this, xhr);
29411             return;
29412         }
29413         
29414         var file = this.renderPreview(response.data);
29415         
29416         this.files.push(file);
29417         
29418         this.arrange();
29419         
29420         this.fireEvent('afterupload', this, xhr);
29421         
29422     },
29423     
29424     xhrOnError : function(xhr)
29425     {
29426         Roo.log('xhr on error');
29427         
29428         var response = Roo.decode(xhr.responseText);
29429           
29430         Roo.log(response);
29431         
29432         this.arrange();
29433     },
29434     
29435     process : function(file)
29436     {
29437         if(this.fireEvent('process', this, file) !== false){
29438             if(this.editable && file.type.indexOf('image') != -1){
29439                 this.fireEvent('edit', this, file);
29440                 return;
29441             }
29442
29443             this.uploadStart(file, false);
29444
29445             return;
29446         }
29447         
29448     },
29449     
29450     uploadStart : function(file, crop)
29451     {
29452         this.xhr = new XMLHttpRequest();
29453         
29454         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29455             this.arrange();
29456             return;
29457         }
29458         
29459         file.xhr = this.xhr;
29460             
29461         this.managerEl.createChild({
29462             tag : 'div',
29463             cls : 'roo-document-manager-loading',
29464             cn : [
29465                 {
29466                     tag : 'div',
29467                     tooltip : file.name,
29468                     cls : 'roo-document-manager-thumb',
29469                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29470                 }
29471             ]
29472
29473         });
29474
29475         this.xhr.open(this.method, this.url, true);
29476         
29477         var headers = {
29478             "Accept": "application/json",
29479             "Cache-Control": "no-cache",
29480             "X-Requested-With": "XMLHttpRequest"
29481         };
29482         
29483         for (var headerName in headers) {
29484             var headerValue = headers[headerName];
29485             if (headerValue) {
29486                 this.xhr.setRequestHeader(headerName, headerValue);
29487             }
29488         }
29489         
29490         var _this = this;
29491         
29492         this.xhr.onload = function()
29493         {
29494             _this.xhrOnLoad(_this.xhr);
29495         }
29496         
29497         this.xhr.onerror = function()
29498         {
29499             _this.xhrOnError(_this.xhr);
29500         }
29501         
29502         var formData = new FormData();
29503
29504         formData.append('returnHTML', 'NO');
29505         
29506         if(crop){
29507             formData.append('crop', crop);
29508         }
29509         
29510         formData.append(this.paramName, file, file.name);
29511         
29512         var options = {
29513             file : file, 
29514             manually : false
29515         };
29516         
29517         if(this.fireEvent('prepare', this, formData, options) != false){
29518             
29519             if(options.manually){
29520                 return;
29521             }
29522             
29523             this.xhr.send(formData);
29524             return;
29525         };
29526         
29527         this.uploadCancel();
29528     },
29529     
29530     uploadCancel : function()
29531     {
29532         if (this.xhr) {
29533             this.xhr.abort();
29534         }
29535         
29536         this.delegates = [];
29537         
29538         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29539             el.remove();
29540         }, this);
29541         
29542         this.arrange();
29543     },
29544     
29545     renderPreview : function(file)
29546     {
29547         if(typeof(file.target) != 'undefined' && file.target){
29548             return file;
29549         }
29550         
29551         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29552         
29553         var previewEl = this.managerEl.createChild({
29554             tag : 'div',
29555             cls : 'roo-document-manager-preview',
29556             cn : [
29557                 {
29558                     tag : 'div',
29559                     tooltip : file[this.toolTipName],
29560                     cls : 'roo-document-manager-thumb',
29561                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29562                 },
29563                 {
29564                     tag : 'button',
29565                     cls : 'close',
29566                     html : '<i class="fa fa-times-circle"></i>'
29567                 }
29568             ]
29569         });
29570
29571         var close = previewEl.select('button.close', true).first();
29572
29573         close.on('click', this.onRemove, this, file);
29574
29575         file.target = previewEl;
29576
29577         var image = previewEl.select('img', true).first();
29578         
29579         var _this = this;
29580         
29581         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29582         
29583         image.on('click', this.onClick, this, file);
29584         
29585         this.fireEvent('previewrendered', this, file);
29586         
29587         return file;
29588         
29589     },
29590     
29591     onPreviewLoad : function(file, image)
29592     {
29593         if(typeof(file.target) == 'undefined' || !file.target){
29594             return;
29595         }
29596         
29597         var width = image.dom.naturalWidth || image.dom.width;
29598         var height = image.dom.naturalHeight || image.dom.height;
29599         
29600         if(!this.previewResize) {
29601             return;
29602         }
29603         
29604         if(width > height){
29605             file.target.addClass('wide');
29606             return;
29607         }
29608         
29609         file.target.addClass('tall');
29610         return;
29611         
29612     },
29613     
29614     uploadFromSource : function(file, crop)
29615     {
29616         this.xhr = new XMLHttpRequest();
29617         
29618         this.managerEl.createChild({
29619             tag : 'div',
29620             cls : 'roo-document-manager-loading',
29621             cn : [
29622                 {
29623                     tag : 'div',
29624                     tooltip : file.name,
29625                     cls : 'roo-document-manager-thumb',
29626                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29627                 }
29628             ]
29629
29630         });
29631
29632         this.xhr.open(this.method, this.url, true);
29633         
29634         var headers = {
29635             "Accept": "application/json",
29636             "Cache-Control": "no-cache",
29637             "X-Requested-With": "XMLHttpRequest"
29638         };
29639         
29640         for (var headerName in headers) {
29641             var headerValue = headers[headerName];
29642             if (headerValue) {
29643                 this.xhr.setRequestHeader(headerName, headerValue);
29644             }
29645         }
29646         
29647         var _this = this;
29648         
29649         this.xhr.onload = function()
29650         {
29651             _this.xhrOnLoad(_this.xhr);
29652         }
29653         
29654         this.xhr.onerror = function()
29655         {
29656             _this.xhrOnError(_this.xhr);
29657         }
29658         
29659         var formData = new FormData();
29660
29661         formData.append('returnHTML', 'NO');
29662         
29663         formData.append('crop', crop);
29664         
29665         if(typeof(file.filename) != 'undefined'){
29666             formData.append('filename', file.filename);
29667         }
29668         
29669         if(typeof(file.mimetype) != 'undefined'){
29670             formData.append('mimetype', file.mimetype);
29671         }
29672         
29673         Roo.log(formData);
29674         
29675         if(this.fireEvent('prepare', this, formData) != false){
29676             this.xhr.send(formData);
29677         };
29678     }
29679 });
29680
29681 /*
29682 * Licence: LGPL
29683 */
29684
29685 /**
29686  * @class Roo.bootstrap.DocumentViewer
29687  * @extends Roo.bootstrap.Component
29688  * Bootstrap DocumentViewer class
29689  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29690  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29691  * 
29692  * @constructor
29693  * Create a new DocumentViewer
29694  * @param {Object} config The config object
29695  */
29696
29697 Roo.bootstrap.DocumentViewer = function(config){
29698     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29699     
29700     this.addEvents({
29701         /**
29702          * @event initial
29703          * Fire after initEvent
29704          * @param {Roo.bootstrap.DocumentViewer} this
29705          */
29706         "initial" : true,
29707         /**
29708          * @event click
29709          * Fire after click
29710          * @param {Roo.bootstrap.DocumentViewer} this
29711          */
29712         "click" : true,
29713         /**
29714          * @event download
29715          * Fire after download button
29716          * @param {Roo.bootstrap.DocumentViewer} this
29717          */
29718         "download" : true,
29719         /**
29720          * @event trash
29721          * Fire after trash button
29722          * @param {Roo.bootstrap.DocumentViewer} this
29723          */
29724         "trash" : true
29725         
29726     });
29727 };
29728
29729 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29730     
29731     showDownload : true,
29732     
29733     showTrash : true,
29734     
29735     getAutoCreate : function()
29736     {
29737         var cfg = {
29738             tag : 'div',
29739             cls : 'roo-document-viewer',
29740             cn : [
29741                 {
29742                     tag : 'div',
29743                     cls : 'roo-document-viewer-body',
29744                     cn : [
29745                         {
29746                             tag : 'div',
29747                             cls : 'roo-document-viewer-thumb',
29748                             cn : [
29749                                 {
29750                                     tag : 'img',
29751                                     cls : 'roo-document-viewer-image'
29752                                 }
29753                             ]
29754                         }
29755                     ]
29756                 },
29757                 {
29758                     tag : 'div',
29759                     cls : 'roo-document-viewer-footer',
29760                     cn : {
29761                         tag : 'div',
29762                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29763                         cn : [
29764                             {
29765                                 tag : 'div',
29766                                 cls : 'btn-group roo-document-viewer-download',
29767                                 cn : [
29768                                     {
29769                                         tag : 'button',
29770                                         cls : 'btn btn-default',
29771                                         html : '<i class="fa fa-download"></i>'
29772                                     }
29773                                 ]
29774                             },
29775                             {
29776                                 tag : 'div',
29777                                 cls : 'btn-group roo-document-viewer-trash',
29778                                 cn : [
29779                                     {
29780                                         tag : 'button',
29781                                         cls : 'btn btn-default',
29782                                         html : '<i class="fa fa-trash"></i>'
29783                                     }
29784                                 ]
29785                             }
29786                         ]
29787                     }
29788                 }
29789             ]
29790         };
29791         
29792         return cfg;
29793     },
29794     
29795     initEvents : function()
29796     {
29797         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29798         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29799         
29800         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29801         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29802         
29803         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29804         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29805         
29806         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29807         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29808         
29809         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29810         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29811         
29812         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29813         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29814         
29815         this.bodyEl.on('click', this.onClick, this);
29816         this.downloadBtn.on('click', this.onDownload, this);
29817         this.trashBtn.on('click', this.onTrash, this);
29818         
29819         this.downloadBtn.hide();
29820         this.trashBtn.hide();
29821         
29822         if(this.showDownload){
29823             this.downloadBtn.show();
29824         }
29825         
29826         if(this.showTrash){
29827             this.trashBtn.show();
29828         }
29829         
29830         if(!this.showDownload && !this.showTrash) {
29831             this.footerEl.hide();
29832         }
29833         
29834     },
29835     
29836     initial : function()
29837     {
29838         this.fireEvent('initial', this);
29839         
29840     },
29841     
29842     onClick : function(e)
29843     {
29844         e.preventDefault();
29845         
29846         this.fireEvent('click', this);
29847     },
29848     
29849     onDownload : function(e)
29850     {
29851         e.preventDefault();
29852         
29853         this.fireEvent('download', this);
29854     },
29855     
29856     onTrash : function(e)
29857     {
29858         e.preventDefault();
29859         
29860         this.fireEvent('trash', this);
29861     }
29862     
29863 });
29864 /*
29865  * - LGPL
29866  *
29867  * nav progress bar
29868  * 
29869  */
29870
29871 /**
29872  * @class Roo.bootstrap.NavProgressBar
29873  * @extends Roo.bootstrap.Component
29874  * Bootstrap NavProgressBar class
29875  * 
29876  * @constructor
29877  * Create a new nav progress bar
29878  * @param {Object} config The config object
29879  */
29880
29881 Roo.bootstrap.NavProgressBar = function(config){
29882     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29883
29884     this.bullets = this.bullets || [];
29885    
29886 //    Roo.bootstrap.NavProgressBar.register(this);
29887      this.addEvents({
29888         /**
29889              * @event changed
29890              * Fires when the active item changes
29891              * @param {Roo.bootstrap.NavProgressBar} this
29892              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29893              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29894          */
29895         'changed': true
29896      });
29897     
29898 };
29899
29900 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29901     
29902     bullets : [],
29903     barItems : [],
29904     
29905     getAutoCreate : function()
29906     {
29907         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29908         
29909         cfg = {
29910             tag : 'div',
29911             cls : 'roo-navigation-bar-group',
29912             cn : [
29913                 {
29914                     tag : 'div',
29915                     cls : 'roo-navigation-top-bar'
29916                 },
29917                 {
29918                     tag : 'div',
29919                     cls : 'roo-navigation-bullets-bar',
29920                     cn : [
29921                         {
29922                             tag : 'ul',
29923                             cls : 'roo-navigation-bar'
29924                         }
29925                     ]
29926                 },
29927                 
29928                 {
29929                     tag : 'div',
29930                     cls : 'roo-navigation-bottom-bar'
29931                 }
29932             ]
29933             
29934         };
29935         
29936         return cfg;
29937         
29938     },
29939     
29940     initEvents: function() 
29941     {
29942         
29943     },
29944     
29945     onRender : function(ct, position) 
29946     {
29947         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29948         
29949         if(this.bullets.length){
29950             Roo.each(this.bullets, function(b){
29951                this.addItem(b);
29952             }, this);
29953         }
29954         
29955         this.format();
29956         
29957     },
29958     
29959     addItem : function(cfg)
29960     {
29961         var item = new Roo.bootstrap.NavProgressItem(cfg);
29962         
29963         item.parentId = this.id;
29964         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29965         
29966         if(cfg.html){
29967             var top = new Roo.bootstrap.Element({
29968                 tag : 'div',
29969                 cls : 'roo-navigation-bar-text'
29970             });
29971             
29972             var bottom = new Roo.bootstrap.Element({
29973                 tag : 'div',
29974                 cls : 'roo-navigation-bar-text'
29975             });
29976             
29977             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29978             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29979             
29980             var topText = new Roo.bootstrap.Element({
29981                 tag : 'span',
29982                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29983             });
29984             
29985             var bottomText = new Roo.bootstrap.Element({
29986                 tag : 'span',
29987                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29988             });
29989             
29990             topText.onRender(top.el, null);
29991             bottomText.onRender(bottom.el, null);
29992             
29993             item.topEl = top;
29994             item.bottomEl = bottom;
29995         }
29996         
29997         this.barItems.push(item);
29998         
29999         return item;
30000     },
30001     
30002     getActive : function()
30003     {
30004         var active = false;
30005         
30006         Roo.each(this.barItems, function(v){
30007             
30008             if (!v.isActive()) {
30009                 return;
30010             }
30011             
30012             active = v;
30013             return false;
30014             
30015         });
30016         
30017         return active;
30018     },
30019     
30020     setActiveItem : function(item)
30021     {
30022         var prev = false;
30023         
30024         Roo.each(this.barItems, function(v){
30025             if (v.rid == item.rid) {
30026                 return ;
30027             }
30028             
30029             if (v.isActive()) {
30030                 v.setActive(false);
30031                 prev = v;
30032             }
30033         });
30034
30035         item.setActive(true);
30036         
30037         this.fireEvent('changed', this, item, prev);
30038     },
30039     
30040     getBarItem: function(rid)
30041     {
30042         var ret = false;
30043         
30044         Roo.each(this.barItems, function(e) {
30045             if (e.rid != rid) {
30046                 return;
30047             }
30048             
30049             ret =  e;
30050             return false;
30051         });
30052         
30053         return ret;
30054     },
30055     
30056     indexOfItem : function(item)
30057     {
30058         var index = false;
30059         
30060         Roo.each(this.barItems, function(v, i){
30061             
30062             if (v.rid != item.rid) {
30063                 return;
30064             }
30065             
30066             index = i;
30067             return false
30068         });
30069         
30070         return index;
30071     },
30072     
30073     setActiveNext : function()
30074     {
30075         var i = this.indexOfItem(this.getActive());
30076         
30077         if (i > this.barItems.length) {
30078             return;
30079         }
30080         
30081         this.setActiveItem(this.barItems[i+1]);
30082     },
30083     
30084     setActivePrev : function()
30085     {
30086         var i = this.indexOfItem(this.getActive());
30087         
30088         if (i  < 1) {
30089             return;
30090         }
30091         
30092         this.setActiveItem(this.barItems[i-1]);
30093     },
30094     
30095     format : function()
30096     {
30097         if(!this.barItems.length){
30098             return;
30099         }
30100      
30101         var width = 100 / this.barItems.length;
30102         
30103         Roo.each(this.barItems, function(i){
30104             i.el.setStyle('width', width + '%');
30105             i.topEl.el.setStyle('width', width + '%');
30106             i.bottomEl.el.setStyle('width', width + '%');
30107         }, this);
30108         
30109     }
30110     
30111 });
30112 /*
30113  * - LGPL
30114  *
30115  * Nav Progress Item
30116  * 
30117  */
30118
30119 /**
30120  * @class Roo.bootstrap.NavProgressItem
30121  * @extends Roo.bootstrap.Component
30122  * Bootstrap NavProgressItem class
30123  * @cfg {String} rid the reference id
30124  * @cfg {Boolean} active (true|false) Is item active default false
30125  * @cfg {Boolean} disabled (true|false) Is item active default false
30126  * @cfg {String} html
30127  * @cfg {String} position (top|bottom) text position default bottom
30128  * @cfg {String} icon show icon instead of number
30129  * 
30130  * @constructor
30131  * Create a new NavProgressItem
30132  * @param {Object} config The config object
30133  */
30134 Roo.bootstrap.NavProgressItem = function(config){
30135     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30136     this.addEvents({
30137         // raw events
30138         /**
30139          * @event click
30140          * The raw click event for the entire grid.
30141          * @param {Roo.bootstrap.NavProgressItem} this
30142          * @param {Roo.EventObject} e
30143          */
30144         "click" : true
30145     });
30146    
30147 };
30148
30149 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30150     
30151     rid : '',
30152     active : false,
30153     disabled : false,
30154     html : '',
30155     position : 'bottom',
30156     icon : false,
30157     
30158     getAutoCreate : function()
30159     {
30160         var iconCls = 'roo-navigation-bar-item-icon';
30161         
30162         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30163         
30164         var cfg = {
30165             tag: 'li',
30166             cls: 'roo-navigation-bar-item',
30167             cn : [
30168                 {
30169                     tag : 'i',
30170                     cls : iconCls
30171                 }
30172             ]
30173         };
30174         
30175         if(this.active){
30176             cfg.cls += ' active';
30177         }
30178         if(this.disabled){
30179             cfg.cls += ' disabled';
30180         }
30181         
30182         return cfg;
30183     },
30184     
30185     disable : function()
30186     {
30187         this.setDisabled(true);
30188     },
30189     
30190     enable : function()
30191     {
30192         this.setDisabled(false);
30193     },
30194     
30195     initEvents: function() 
30196     {
30197         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30198         
30199         this.iconEl.on('click', this.onClick, this);
30200     },
30201     
30202     onClick : function(e)
30203     {
30204         e.preventDefault();
30205         
30206         if(this.disabled){
30207             return;
30208         }
30209         
30210         if(this.fireEvent('click', this, e) === false){
30211             return;
30212         };
30213         
30214         this.parent().setActiveItem(this);
30215     },
30216     
30217     isActive: function () 
30218     {
30219         return this.active;
30220     },
30221     
30222     setActive : function(state)
30223     {
30224         if(this.active == state){
30225             return;
30226         }
30227         
30228         this.active = state;
30229         
30230         if (state) {
30231             this.el.addClass('active');
30232             return;
30233         }
30234         
30235         this.el.removeClass('active');
30236         
30237         return;
30238     },
30239     
30240     setDisabled : function(state)
30241     {
30242         if(this.disabled == state){
30243             return;
30244         }
30245         
30246         this.disabled = state;
30247         
30248         if (state) {
30249             this.el.addClass('disabled');
30250             return;
30251         }
30252         
30253         this.el.removeClass('disabled');
30254     },
30255     
30256     tooltipEl : function()
30257     {
30258         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30259     }
30260 });
30261  
30262
30263  /*
30264  * - LGPL
30265  *
30266  * FieldLabel
30267  * 
30268  */
30269
30270 /**
30271  * @class Roo.bootstrap.FieldLabel
30272  * @extends Roo.bootstrap.Component
30273  * Bootstrap FieldLabel class
30274  * @cfg {String} html contents of the element
30275  * @cfg {String} tag tag of the element default label
30276  * @cfg {String} cls class of the element
30277  * @cfg {String} target label target 
30278  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30279  * @cfg {String} invalidClass default "text-warning"
30280  * @cfg {String} validClass default "text-success"
30281  * @cfg {String} iconTooltip default "This field is required"
30282  * @cfg {String} indicatorpos (left|right) default left
30283  * 
30284  * @constructor
30285  * Create a new FieldLabel
30286  * @param {Object} config The config object
30287  */
30288
30289 Roo.bootstrap.FieldLabel = function(config){
30290     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30291     
30292     this.addEvents({
30293             /**
30294              * @event invalid
30295              * Fires after the field has been marked as invalid.
30296              * @param {Roo.form.FieldLabel} this
30297              * @param {String} msg The validation message
30298              */
30299             invalid : true,
30300             /**
30301              * @event valid
30302              * Fires after the field has been validated with no errors.
30303              * @param {Roo.form.FieldLabel} this
30304              */
30305             valid : true
30306         });
30307 };
30308
30309 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30310     
30311     tag: 'label',
30312     cls: '',
30313     html: '',
30314     target: '',
30315     allowBlank : true,
30316     invalidClass : 'has-warning',
30317     validClass : 'has-success',
30318     iconTooltip : 'This field is required',
30319     indicatorpos : 'left',
30320     
30321     getAutoCreate : function(){
30322         
30323         var cls = "";
30324         if (!this.allowBlank) {
30325             cls  = "visible";
30326         }
30327         
30328         var cfg = {
30329             tag : this.tag,
30330             cls : 'roo-bootstrap-field-label ' + this.cls,
30331             for : this.target,
30332             cn : [
30333                 {
30334                     tag : 'i',
30335                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30336                     tooltip : this.iconTooltip
30337                 },
30338                 {
30339                     tag : 'span',
30340                     html : this.html
30341                 }
30342             ] 
30343         };
30344         
30345         if(this.indicatorpos == 'right'){
30346             var cfg = {
30347                 tag : this.tag,
30348                 cls : 'roo-bootstrap-field-label ' + this.cls,
30349                 for : this.target,
30350                 cn : [
30351                     {
30352                         tag : 'span',
30353                         html : this.html
30354                     },
30355                     {
30356                         tag : 'i',
30357                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30358                         tooltip : this.iconTooltip
30359                     }
30360                 ] 
30361             };
30362         }
30363         
30364         return cfg;
30365     },
30366     
30367     initEvents: function() 
30368     {
30369         Roo.bootstrap.Element.superclass.initEvents.call(this);
30370         
30371         this.indicator = this.indicatorEl();
30372         
30373         if(this.indicator){
30374             this.indicator.removeClass('visible');
30375             this.indicator.addClass('invisible');
30376         }
30377         
30378         Roo.bootstrap.FieldLabel.register(this);
30379     },
30380     
30381     indicatorEl : function()
30382     {
30383         var indicator = this.el.select('i.roo-required-indicator',true).first();
30384         
30385         if(!indicator){
30386             return false;
30387         }
30388         
30389         return indicator;
30390         
30391     },
30392     
30393     /**
30394      * Mark this field as valid
30395      */
30396     markValid : function()
30397     {
30398         if(this.indicator){
30399             this.indicator.removeClass('visible');
30400             this.indicator.addClass('invisible');
30401         }
30402         
30403         this.el.removeClass(this.invalidClass);
30404         
30405         this.el.addClass(this.validClass);
30406         
30407         this.fireEvent('valid', this);
30408     },
30409     
30410     /**
30411      * Mark this field as invalid
30412      * @param {String} msg The validation message
30413      */
30414     markInvalid : function(msg)
30415     {
30416         if(this.indicator){
30417             this.indicator.removeClass('invisible');
30418             this.indicator.addClass('visible');
30419         }
30420         
30421         this.el.removeClass(this.validClass);
30422         
30423         this.el.addClass(this.invalidClass);
30424         
30425         this.fireEvent('invalid', this, msg);
30426     }
30427     
30428    
30429 });
30430
30431 Roo.apply(Roo.bootstrap.FieldLabel, {
30432     
30433     groups: {},
30434     
30435      /**
30436     * register a FieldLabel Group
30437     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30438     */
30439     register : function(label)
30440     {
30441         if(this.groups.hasOwnProperty(label.target)){
30442             return;
30443         }
30444      
30445         this.groups[label.target] = label;
30446         
30447     },
30448     /**
30449     * fetch a FieldLabel Group based on the target
30450     * @param {string} target
30451     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30452     */
30453     get: function(target) {
30454         if (typeof(this.groups[target]) == 'undefined') {
30455             return false;
30456         }
30457         
30458         return this.groups[target] ;
30459     }
30460 });
30461
30462  
30463
30464  /*
30465  * - LGPL
30466  *
30467  * page DateSplitField.
30468  * 
30469  */
30470
30471
30472 /**
30473  * @class Roo.bootstrap.DateSplitField
30474  * @extends Roo.bootstrap.Component
30475  * Bootstrap DateSplitField class
30476  * @cfg {string} fieldLabel - the label associated
30477  * @cfg {Number} labelWidth set the width of label (0-12)
30478  * @cfg {String} labelAlign (top|left)
30479  * @cfg {Boolean} dayAllowBlank (true|false) default false
30480  * @cfg {Boolean} monthAllowBlank (true|false) default false
30481  * @cfg {Boolean} yearAllowBlank (true|false) default false
30482  * @cfg {string} dayPlaceholder 
30483  * @cfg {string} monthPlaceholder
30484  * @cfg {string} yearPlaceholder
30485  * @cfg {string} dayFormat default 'd'
30486  * @cfg {string} monthFormat default 'm'
30487  * @cfg {string} yearFormat default 'Y'
30488  * @cfg {Number} labellg set the width of label (1-12)
30489  * @cfg {Number} labelmd set the width of label (1-12)
30490  * @cfg {Number} labelsm set the width of label (1-12)
30491  * @cfg {Number} labelxs set the width of label (1-12)
30492
30493  *     
30494  * @constructor
30495  * Create a new DateSplitField
30496  * @param {Object} config The config object
30497  */
30498
30499 Roo.bootstrap.DateSplitField = function(config){
30500     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30501     
30502     this.addEvents({
30503         // raw events
30504          /**
30505          * @event years
30506          * getting the data of years
30507          * @param {Roo.bootstrap.DateSplitField} this
30508          * @param {Object} years
30509          */
30510         "years" : true,
30511         /**
30512          * @event days
30513          * getting the data of days
30514          * @param {Roo.bootstrap.DateSplitField} this
30515          * @param {Object} days
30516          */
30517         "days" : true,
30518         /**
30519          * @event invalid
30520          * Fires after the field has been marked as invalid.
30521          * @param {Roo.form.Field} this
30522          * @param {String} msg The validation message
30523          */
30524         invalid : true,
30525        /**
30526          * @event valid
30527          * Fires after the field has been validated with no errors.
30528          * @param {Roo.form.Field} this
30529          */
30530         valid : true
30531     });
30532 };
30533
30534 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30535     
30536     fieldLabel : '',
30537     labelAlign : 'top',
30538     labelWidth : 3,
30539     dayAllowBlank : false,
30540     monthAllowBlank : false,
30541     yearAllowBlank : false,
30542     dayPlaceholder : '',
30543     monthPlaceholder : '',
30544     yearPlaceholder : '',
30545     dayFormat : 'd',
30546     monthFormat : 'm',
30547     yearFormat : 'Y',
30548     isFormField : true,
30549     labellg : 0,
30550     labelmd : 0,
30551     labelsm : 0,
30552     labelxs : 0,
30553     
30554     getAutoCreate : function()
30555     {
30556         var cfg = {
30557             tag : 'div',
30558             cls : 'row roo-date-split-field-group',
30559             cn : [
30560                 {
30561                     tag : 'input',
30562                     type : 'hidden',
30563                     cls : 'form-hidden-field roo-date-split-field-group-value',
30564                     name : this.name
30565                 }
30566             ]
30567         };
30568         
30569         var labelCls = 'col-md-12';
30570         var contentCls = 'col-md-4';
30571         
30572         if(this.fieldLabel){
30573             
30574             var label = {
30575                 tag : 'div',
30576                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30577                 cn : [
30578                     {
30579                         tag : 'label',
30580                         html : this.fieldLabel
30581                     }
30582                 ]
30583             };
30584             
30585             if(this.labelAlign == 'left'){
30586             
30587                 if(this.labelWidth > 12){
30588                     label.style = "width: " + this.labelWidth + 'px';
30589                 }
30590
30591                 if(this.labelWidth < 13 && this.labelmd == 0){
30592                     this.labelmd = this.labelWidth;
30593                 }
30594
30595                 if(this.labellg > 0){
30596                     labelCls = ' col-lg-' + this.labellg;
30597                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30598                 }
30599
30600                 if(this.labelmd > 0){
30601                     labelCls = ' col-md-' + this.labelmd;
30602                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30603                 }
30604
30605                 if(this.labelsm > 0){
30606                     labelCls = ' col-sm-' + this.labelsm;
30607                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30608                 }
30609
30610                 if(this.labelxs > 0){
30611                     labelCls = ' col-xs-' + this.labelxs;
30612                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30613                 }
30614             }
30615             
30616             label.cls += ' ' + labelCls;
30617             
30618             cfg.cn.push(label);
30619         }
30620         
30621         Roo.each(['day', 'month', 'year'], function(t){
30622             cfg.cn.push({
30623                 tag : 'div',
30624                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30625             });
30626         }, this);
30627         
30628         return cfg;
30629     },
30630     
30631     inputEl: function ()
30632     {
30633         return this.el.select('.roo-date-split-field-group-value', true).first();
30634     },
30635     
30636     onRender : function(ct, position) 
30637     {
30638         var _this = this;
30639         
30640         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30641         
30642         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30643         
30644         this.dayField = new Roo.bootstrap.ComboBox({
30645             allowBlank : this.dayAllowBlank,
30646             alwaysQuery : true,
30647             displayField : 'value',
30648             editable : false,
30649             fieldLabel : '',
30650             forceSelection : true,
30651             mode : 'local',
30652             placeholder : this.dayPlaceholder,
30653             selectOnFocus : true,
30654             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30655             triggerAction : 'all',
30656             typeAhead : true,
30657             valueField : 'value',
30658             store : new Roo.data.SimpleStore({
30659                 data : (function() {    
30660                     var days = [];
30661                     _this.fireEvent('days', _this, days);
30662                     return days;
30663                 })(),
30664                 fields : [ 'value' ]
30665             }),
30666             listeners : {
30667                 select : function (_self, record, index)
30668                 {
30669                     _this.setValue(_this.getValue());
30670                 }
30671             }
30672         });
30673
30674         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30675         
30676         this.monthField = new Roo.bootstrap.MonthField({
30677             after : '<i class=\"fa fa-calendar\"></i>',
30678             allowBlank : this.monthAllowBlank,
30679             placeholder : this.monthPlaceholder,
30680             readOnly : true,
30681             listeners : {
30682                 render : function (_self)
30683                 {
30684                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30685                         e.preventDefault();
30686                         _self.focus();
30687                     });
30688                 },
30689                 select : function (_self, oldvalue, newvalue)
30690                 {
30691                     _this.setValue(_this.getValue());
30692                 }
30693             }
30694         });
30695         
30696         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30697         
30698         this.yearField = new Roo.bootstrap.ComboBox({
30699             allowBlank : this.yearAllowBlank,
30700             alwaysQuery : true,
30701             displayField : 'value',
30702             editable : false,
30703             fieldLabel : '',
30704             forceSelection : true,
30705             mode : 'local',
30706             placeholder : this.yearPlaceholder,
30707             selectOnFocus : true,
30708             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30709             triggerAction : 'all',
30710             typeAhead : true,
30711             valueField : 'value',
30712             store : new Roo.data.SimpleStore({
30713                 data : (function() {
30714                     var years = [];
30715                     _this.fireEvent('years', _this, years);
30716                     return years;
30717                 })(),
30718                 fields : [ 'value' ]
30719             }),
30720             listeners : {
30721                 select : function (_self, record, index)
30722                 {
30723                     _this.setValue(_this.getValue());
30724                 }
30725             }
30726         });
30727
30728         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30729     },
30730     
30731     setValue : function(v, format)
30732     {
30733         this.inputEl.dom.value = v;
30734         
30735         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30736         
30737         var d = Date.parseDate(v, f);
30738         
30739         if(!d){
30740             this.validate();
30741             return;
30742         }
30743         
30744         this.setDay(d.format(this.dayFormat));
30745         this.setMonth(d.format(this.monthFormat));
30746         this.setYear(d.format(this.yearFormat));
30747         
30748         this.validate();
30749         
30750         return;
30751     },
30752     
30753     setDay : function(v)
30754     {
30755         this.dayField.setValue(v);
30756         this.inputEl.dom.value = this.getValue();
30757         this.validate();
30758         return;
30759     },
30760     
30761     setMonth : function(v)
30762     {
30763         this.monthField.setValue(v, true);
30764         this.inputEl.dom.value = this.getValue();
30765         this.validate();
30766         return;
30767     },
30768     
30769     setYear : function(v)
30770     {
30771         this.yearField.setValue(v);
30772         this.inputEl.dom.value = this.getValue();
30773         this.validate();
30774         return;
30775     },
30776     
30777     getDay : function()
30778     {
30779         return this.dayField.getValue();
30780     },
30781     
30782     getMonth : function()
30783     {
30784         return this.monthField.getValue();
30785     },
30786     
30787     getYear : function()
30788     {
30789         return this.yearField.getValue();
30790     },
30791     
30792     getValue : function()
30793     {
30794         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30795         
30796         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30797         
30798         return date;
30799     },
30800     
30801     reset : function()
30802     {
30803         this.setDay('');
30804         this.setMonth('');
30805         this.setYear('');
30806         this.inputEl.dom.value = '';
30807         this.validate();
30808         return;
30809     },
30810     
30811     validate : function()
30812     {
30813         var d = this.dayField.validate();
30814         var m = this.monthField.validate();
30815         var y = this.yearField.validate();
30816         
30817         var valid = true;
30818         
30819         if(
30820                 (!this.dayAllowBlank && !d) ||
30821                 (!this.monthAllowBlank && !m) ||
30822                 (!this.yearAllowBlank && !y)
30823         ){
30824             valid = false;
30825         }
30826         
30827         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30828             return valid;
30829         }
30830         
30831         if(valid){
30832             this.markValid();
30833             return valid;
30834         }
30835         
30836         this.markInvalid();
30837         
30838         return valid;
30839     },
30840     
30841     markValid : function()
30842     {
30843         
30844         var label = this.el.select('label', true).first();
30845         var icon = this.el.select('i.fa-star', true).first();
30846
30847         if(label && icon){
30848             icon.remove();
30849         }
30850         
30851         this.fireEvent('valid', this);
30852     },
30853     
30854      /**
30855      * Mark this field as invalid
30856      * @param {String} msg The validation message
30857      */
30858     markInvalid : function(msg)
30859     {
30860         
30861         var label = this.el.select('label', true).first();
30862         var icon = this.el.select('i.fa-star', true).first();
30863
30864         if(label && !icon){
30865             this.el.select('.roo-date-split-field-label', true).createChild({
30866                 tag : 'i',
30867                 cls : 'text-danger fa fa-lg fa-star',
30868                 tooltip : 'This field is required',
30869                 style : 'margin-right:5px;'
30870             }, label, true);
30871         }
30872         
30873         this.fireEvent('invalid', this, msg);
30874     },
30875     
30876     clearInvalid : function()
30877     {
30878         var label = this.el.select('label', true).first();
30879         var icon = this.el.select('i.fa-star', true).first();
30880
30881         if(label && icon){
30882             icon.remove();
30883         }
30884         
30885         this.fireEvent('valid', this);
30886     },
30887     
30888     getName: function()
30889     {
30890         return this.name;
30891     }
30892     
30893 });
30894
30895  /**
30896  *
30897  * This is based on 
30898  * http://masonry.desandro.com
30899  *
30900  * The idea is to render all the bricks based on vertical width...
30901  *
30902  * The original code extends 'outlayer' - we might need to use that....
30903  * 
30904  */
30905
30906
30907 /**
30908  * @class Roo.bootstrap.LayoutMasonry
30909  * @extends Roo.bootstrap.Component
30910  * Bootstrap Layout Masonry class
30911  * 
30912  * @constructor
30913  * Create a new Element
30914  * @param {Object} config The config object
30915  */
30916
30917 Roo.bootstrap.LayoutMasonry = function(config){
30918     
30919     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30920     
30921     this.bricks = [];
30922     
30923     Roo.bootstrap.LayoutMasonry.register(this);
30924     
30925     this.addEvents({
30926         // raw events
30927         /**
30928          * @event layout
30929          * Fire after layout the items
30930          * @param {Roo.bootstrap.LayoutMasonry} this
30931          * @param {Roo.EventObject} e
30932          */
30933         "layout" : true
30934     });
30935     
30936 };
30937
30938 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30939     
30940     /**
30941      * @cfg {Boolean} isLayoutInstant = no animation?
30942      */   
30943     isLayoutInstant : false, // needed?
30944    
30945     /**
30946      * @cfg {Number} boxWidth  width of the columns
30947      */   
30948     boxWidth : 450,
30949     
30950       /**
30951      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30952      */   
30953     boxHeight : 0,
30954     
30955     /**
30956      * @cfg {Number} padWidth padding below box..
30957      */   
30958     padWidth : 10, 
30959     
30960     /**
30961      * @cfg {Number} gutter gutter width..
30962      */   
30963     gutter : 10,
30964     
30965      /**
30966      * @cfg {Number} maxCols maximum number of columns
30967      */   
30968     
30969     maxCols: 0,
30970     
30971     /**
30972      * @cfg {Boolean} isAutoInitial defalut true
30973      */   
30974     isAutoInitial : true, 
30975     
30976     containerWidth: 0,
30977     
30978     /**
30979      * @cfg {Boolean} isHorizontal defalut false
30980      */   
30981     isHorizontal : false, 
30982
30983     currentSize : null,
30984     
30985     tag: 'div',
30986     
30987     cls: '',
30988     
30989     bricks: null, //CompositeElement
30990     
30991     cols : 1,
30992     
30993     _isLayoutInited : false,
30994     
30995 //    isAlternative : false, // only use for vertical layout...
30996     
30997     /**
30998      * @cfg {Number} alternativePadWidth padding below box..
30999      */   
31000     alternativePadWidth : 50,
31001     
31002     selectedBrick : [],
31003     
31004     getAutoCreate : function(){
31005         
31006         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31007         
31008         var cfg = {
31009             tag: this.tag,
31010             cls: 'blog-masonary-wrapper ' + this.cls,
31011             cn : {
31012                 cls : 'mas-boxes masonary'
31013             }
31014         };
31015         
31016         return cfg;
31017     },
31018     
31019     getChildContainer: function( )
31020     {
31021         if (this.boxesEl) {
31022             return this.boxesEl;
31023         }
31024         
31025         this.boxesEl = this.el.select('.mas-boxes').first();
31026         
31027         return this.boxesEl;
31028     },
31029     
31030     
31031     initEvents : function()
31032     {
31033         var _this = this;
31034         
31035         if(this.isAutoInitial){
31036             Roo.log('hook children rendered');
31037             this.on('childrenrendered', function() {
31038                 Roo.log('children rendered');
31039                 _this.initial();
31040             } ,this);
31041         }
31042     },
31043     
31044     initial : function()
31045     {
31046         this.selectedBrick = [];
31047         
31048         this.currentSize = this.el.getBox(true);
31049         
31050         Roo.EventManager.onWindowResize(this.resize, this); 
31051
31052         if(!this.isAutoInitial){
31053             this.layout();
31054             return;
31055         }
31056         
31057         this.layout();
31058         
31059         return;
31060         //this.layout.defer(500,this);
31061         
31062     },
31063     
31064     resize : function()
31065     {
31066         var cs = this.el.getBox(true);
31067         
31068         if (
31069                 this.currentSize.width == cs.width && 
31070                 this.currentSize.x == cs.x && 
31071                 this.currentSize.height == cs.height && 
31072                 this.currentSize.y == cs.y 
31073         ) {
31074             Roo.log("no change in with or X or Y");
31075             return;
31076         }
31077         
31078         this.currentSize = cs;
31079         
31080         this.layout();
31081         
31082     },
31083     
31084     layout : function()
31085     {   
31086         this._resetLayout();
31087         
31088         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31089         
31090         this.layoutItems( isInstant );
31091       
31092         this._isLayoutInited = true;
31093         
31094         this.fireEvent('layout', this);
31095         
31096     },
31097     
31098     _resetLayout : function()
31099     {
31100         if(this.isHorizontal){
31101             this.horizontalMeasureColumns();
31102             return;
31103         }
31104         
31105         this.verticalMeasureColumns();
31106         
31107     },
31108     
31109     verticalMeasureColumns : function()
31110     {
31111         this.getContainerWidth();
31112         
31113 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31114 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31115 //            return;
31116 //        }
31117         
31118         var boxWidth = this.boxWidth + this.padWidth;
31119         
31120         if(this.containerWidth < this.boxWidth){
31121             boxWidth = this.containerWidth
31122         }
31123         
31124         var containerWidth = this.containerWidth;
31125         
31126         var cols = Math.floor(containerWidth / boxWidth);
31127         
31128         this.cols = Math.max( cols, 1 );
31129         
31130         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31131         
31132         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31133         
31134         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31135         
31136         this.colWidth = boxWidth + avail - this.padWidth;
31137         
31138         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31139         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31140     },
31141     
31142     horizontalMeasureColumns : function()
31143     {
31144         this.getContainerWidth();
31145         
31146         var boxWidth = this.boxWidth;
31147         
31148         if(this.containerWidth < boxWidth){
31149             boxWidth = this.containerWidth;
31150         }
31151         
31152         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31153         
31154         this.el.setHeight(boxWidth);
31155         
31156     },
31157     
31158     getContainerWidth : function()
31159     {
31160         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31161     },
31162     
31163     layoutItems : function( isInstant )
31164     {
31165         Roo.log(this.bricks);
31166         
31167         var items = Roo.apply([], this.bricks);
31168         
31169         if(this.isHorizontal){
31170             this._horizontalLayoutItems( items , isInstant );
31171             return;
31172         }
31173         
31174 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31175 //            this._verticalAlternativeLayoutItems( items , isInstant );
31176 //            return;
31177 //        }
31178         
31179         this._verticalLayoutItems( items , isInstant );
31180         
31181     },
31182     
31183     _verticalLayoutItems : function ( items , isInstant)
31184     {
31185         if ( !items || !items.length ) {
31186             return;
31187         }
31188         
31189         var standard = [
31190             ['xs', 'xs', 'xs', 'tall'],
31191             ['xs', 'xs', 'tall'],
31192             ['xs', 'xs', 'sm'],
31193             ['xs', 'xs', 'xs'],
31194             ['xs', 'tall'],
31195             ['xs', 'sm'],
31196             ['xs', 'xs'],
31197             ['xs'],
31198             
31199             ['sm', 'xs', 'xs'],
31200             ['sm', 'xs'],
31201             ['sm'],
31202             
31203             ['tall', 'xs', 'xs', 'xs'],
31204             ['tall', 'xs', 'xs'],
31205             ['tall', 'xs'],
31206             ['tall']
31207             
31208         ];
31209         
31210         var queue = [];
31211         
31212         var boxes = [];
31213         
31214         var box = [];
31215         
31216         Roo.each(items, function(item, k){
31217             
31218             switch (item.size) {
31219                 // these layouts take up a full box,
31220                 case 'md' :
31221                 case 'md-left' :
31222                 case 'md-right' :
31223                 case 'wide' :
31224                     
31225                     if(box.length){
31226                         boxes.push(box);
31227                         box = [];
31228                     }
31229                     
31230                     boxes.push([item]);
31231                     
31232                     break;
31233                     
31234                 case 'xs' :
31235                 case 'sm' :
31236                 case 'tall' :
31237                     
31238                     box.push(item);
31239                     
31240                     break;
31241                 default :
31242                     break;
31243                     
31244             }
31245             
31246         }, this);
31247         
31248         if(box.length){
31249             boxes.push(box);
31250             box = [];
31251         }
31252         
31253         var filterPattern = function(box, length)
31254         {
31255             if(!box.length){
31256                 return;
31257             }
31258             
31259             var match = false;
31260             
31261             var pattern = box.slice(0, length);
31262             
31263             var format = [];
31264             
31265             Roo.each(pattern, function(i){
31266                 format.push(i.size);
31267             }, this);
31268             
31269             Roo.each(standard, function(s){
31270                 
31271                 if(String(s) != String(format)){
31272                     return;
31273                 }
31274                 
31275                 match = true;
31276                 return false;
31277                 
31278             }, this);
31279             
31280             if(!match && length == 1){
31281                 return;
31282             }
31283             
31284             if(!match){
31285                 filterPattern(box, length - 1);
31286                 return;
31287             }
31288                 
31289             queue.push(pattern);
31290
31291             box = box.slice(length, box.length);
31292
31293             filterPattern(box, 4);
31294
31295             return;
31296             
31297         }
31298         
31299         Roo.each(boxes, function(box, k){
31300             
31301             if(!box.length){
31302                 return;
31303             }
31304             
31305             if(box.length == 1){
31306                 queue.push(box);
31307                 return;
31308             }
31309             
31310             filterPattern(box, 4);
31311             
31312         }, this);
31313         
31314         this._processVerticalLayoutQueue( queue, isInstant );
31315         
31316     },
31317     
31318 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31319 //    {
31320 //        if ( !items || !items.length ) {
31321 //            return;
31322 //        }
31323 //
31324 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31325 //        
31326 //    },
31327     
31328     _horizontalLayoutItems : function ( items , isInstant)
31329     {
31330         if ( !items || !items.length || items.length < 3) {
31331             return;
31332         }
31333         
31334         items.reverse();
31335         
31336         var eItems = items.slice(0, 3);
31337         
31338         items = items.slice(3, items.length);
31339         
31340         var standard = [
31341             ['xs', 'xs', 'xs', 'wide'],
31342             ['xs', 'xs', 'wide'],
31343             ['xs', 'xs', 'sm'],
31344             ['xs', 'xs', 'xs'],
31345             ['xs', 'wide'],
31346             ['xs', 'sm'],
31347             ['xs', 'xs'],
31348             ['xs'],
31349             
31350             ['sm', 'xs', 'xs'],
31351             ['sm', 'xs'],
31352             ['sm'],
31353             
31354             ['wide', 'xs', 'xs', 'xs'],
31355             ['wide', 'xs', 'xs'],
31356             ['wide', 'xs'],
31357             ['wide'],
31358             
31359             ['wide-thin']
31360         ];
31361         
31362         var queue = [];
31363         
31364         var boxes = [];
31365         
31366         var box = [];
31367         
31368         Roo.each(items, function(item, k){
31369             
31370             switch (item.size) {
31371                 case 'md' :
31372                 case 'md-left' :
31373                 case 'md-right' :
31374                 case 'tall' :
31375                     
31376                     if(box.length){
31377                         boxes.push(box);
31378                         box = [];
31379                     }
31380                     
31381                     boxes.push([item]);
31382                     
31383                     break;
31384                     
31385                 case 'xs' :
31386                 case 'sm' :
31387                 case 'wide' :
31388                 case 'wide-thin' :
31389                     
31390                     box.push(item);
31391                     
31392                     break;
31393                 default :
31394                     break;
31395                     
31396             }
31397             
31398         }, this);
31399         
31400         if(box.length){
31401             boxes.push(box);
31402             box = [];
31403         }
31404         
31405         var filterPattern = function(box, length)
31406         {
31407             if(!box.length){
31408                 return;
31409             }
31410             
31411             var match = false;
31412             
31413             var pattern = box.slice(0, length);
31414             
31415             var format = [];
31416             
31417             Roo.each(pattern, function(i){
31418                 format.push(i.size);
31419             }, this);
31420             
31421             Roo.each(standard, function(s){
31422                 
31423                 if(String(s) != String(format)){
31424                     return;
31425                 }
31426                 
31427                 match = true;
31428                 return false;
31429                 
31430             }, this);
31431             
31432             if(!match && length == 1){
31433                 return;
31434             }
31435             
31436             if(!match){
31437                 filterPattern(box, length - 1);
31438                 return;
31439             }
31440                 
31441             queue.push(pattern);
31442
31443             box = box.slice(length, box.length);
31444
31445             filterPattern(box, 4);
31446
31447             return;
31448             
31449         }
31450         
31451         Roo.each(boxes, function(box, k){
31452             
31453             if(!box.length){
31454                 return;
31455             }
31456             
31457             if(box.length == 1){
31458                 queue.push(box);
31459                 return;
31460             }
31461             
31462             filterPattern(box, 4);
31463             
31464         }, this);
31465         
31466         
31467         var prune = [];
31468         
31469         var pos = this.el.getBox(true);
31470         
31471         var minX = pos.x;
31472         
31473         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31474         
31475         var hit_end = false;
31476         
31477         Roo.each(queue, function(box){
31478             
31479             if(hit_end){
31480                 
31481                 Roo.each(box, function(b){
31482                 
31483                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31484                     b.el.hide();
31485
31486                 }, this);
31487
31488                 return;
31489             }
31490             
31491             var mx = 0;
31492             
31493             Roo.each(box, function(b){
31494                 
31495                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31496                 b.el.show();
31497
31498                 mx = Math.max(mx, b.x);
31499                 
31500             }, this);
31501             
31502             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31503             
31504             if(maxX < minX){
31505                 
31506                 Roo.each(box, function(b){
31507                 
31508                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31509                     b.el.hide();
31510                     
31511                 }, this);
31512                 
31513                 hit_end = true;
31514                 
31515                 return;
31516             }
31517             
31518             prune.push(box);
31519             
31520         }, this);
31521         
31522         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31523     },
31524     
31525     /** Sets position of item in DOM
31526     * @param {Element} item
31527     * @param {Number} x - horizontal position
31528     * @param {Number} y - vertical position
31529     * @param {Boolean} isInstant - disables transitions
31530     */
31531     _processVerticalLayoutQueue : function( queue, isInstant )
31532     {
31533         var pos = this.el.getBox(true);
31534         var x = pos.x;
31535         var y = pos.y;
31536         var maxY = [];
31537         
31538         for (var i = 0; i < this.cols; i++){
31539             maxY[i] = pos.y;
31540         }
31541         
31542         Roo.each(queue, function(box, k){
31543             
31544             var col = k % this.cols;
31545             
31546             Roo.each(box, function(b,kk){
31547                 
31548                 b.el.position('absolute');
31549                 
31550                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31551                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31552                 
31553                 if(b.size == 'md-left' || b.size == 'md-right'){
31554                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31555                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31556                 }
31557                 
31558                 b.el.setWidth(width);
31559                 b.el.setHeight(height);
31560                 // iframe?
31561                 b.el.select('iframe',true).setSize(width,height);
31562                 
31563             }, this);
31564             
31565             for (var i = 0; i < this.cols; i++){
31566                 
31567                 if(maxY[i] < maxY[col]){
31568                     col = i;
31569                     continue;
31570                 }
31571                 
31572                 col = Math.min(col, i);
31573                 
31574             }
31575             
31576             x = pos.x + col * (this.colWidth + this.padWidth);
31577             
31578             y = maxY[col];
31579             
31580             var positions = [];
31581             
31582             switch (box.length){
31583                 case 1 :
31584                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31585                     break;
31586                 case 2 :
31587                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31588                     break;
31589                 case 3 :
31590                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31591                     break;
31592                 case 4 :
31593                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31594                     break;
31595                 default :
31596                     break;
31597             }
31598             
31599             Roo.each(box, function(b,kk){
31600                 
31601                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31602                 
31603                 var sz = b.el.getSize();
31604                 
31605                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31606                 
31607             }, this);
31608             
31609         }, this);
31610         
31611         var mY = 0;
31612         
31613         for (var i = 0; i < this.cols; i++){
31614             mY = Math.max(mY, maxY[i]);
31615         }
31616         
31617         this.el.setHeight(mY - pos.y);
31618         
31619     },
31620     
31621 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31622 //    {
31623 //        var pos = this.el.getBox(true);
31624 //        var x = pos.x;
31625 //        var y = pos.y;
31626 //        var maxX = pos.right;
31627 //        
31628 //        var maxHeight = 0;
31629 //        
31630 //        Roo.each(items, function(item, k){
31631 //            
31632 //            var c = k % 2;
31633 //            
31634 //            item.el.position('absolute');
31635 //                
31636 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31637 //
31638 //            item.el.setWidth(width);
31639 //
31640 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31641 //
31642 //            item.el.setHeight(height);
31643 //            
31644 //            if(c == 0){
31645 //                item.el.setXY([x, y], isInstant ? false : true);
31646 //            } else {
31647 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31648 //            }
31649 //            
31650 //            y = y + height + this.alternativePadWidth;
31651 //            
31652 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31653 //            
31654 //        }, this);
31655 //        
31656 //        this.el.setHeight(maxHeight);
31657 //        
31658 //    },
31659     
31660     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31661     {
31662         var pos = this.el.getBox(true);
31663         
31664         var minX = pos.x;
31665         var minY = pos.y;
31666         
31667         var maxX = pos.right;
31668         
31669         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31670         
31671         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31672         
31673         Roo.each(queue, function(box, k){
31674             
31675             Roo.each(box, function(b, kk){
31676                 
31677                 b.el.position('absolute');
31678                 
31679                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31680                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31681                 
31682                 if(b.size == 'md-left' || b.size == 'md-right'){
31683                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31684                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31685                 }
31686                 
31687                 b.el.setWidth(width);
31688                 b.el.setHeight(height);
31689                 
31690             }, this);
31691             
31692             if(!box.length){
31693                 return;
31694             }
31695             
31696             var positions = [];
31697             
31698             switch (box.length){
31699                 case 1 :
31700                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31701                     break;
31702                 case 2 :
31703                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31704                     break;
31705                 case 3 :
31706                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31707                     break;
31708                 case 4 :
31709                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31710                     break;
31711                 default :
31712                     break;
31713             }
31714             
31715             Roo.each(box, function(b,kk){
31716                 
31717                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31718                 
31719                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31720                 
31721             }, this);
31722             
31723         }, this);
31724         
31725     },
31726     
31727     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31728     {
31729         Roo.each(eItems, function(b,k){
31730             
31731             b.size = (k == 0) ? 'sm' : 'xs';
31732             b.x = (k == 0) ? 2 : 1;
31733             b.y = (k == 0) ? 2 : 1;
31734             
31735             b.el.position('absolute');
31736             
31737             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31738                 
31739             b.el.setWidth(width);
31740             
31741             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31742             
31743             b.el.setHeight(height);
31744             
31745         }, this);
31746
31747         var positions = [];
31748         
31749         positions.push({
31750             x : maxX - this.unitWidth * 2 - this.gutter,
31751             y : minY
31752         });
31753         
31754         positions.push({
31755             x : maxX - this.unitWidth,
31756             y : minY + (this.unitWidth + this.gutter) * 2
31757         });
31758         
31759         positions.push({
31760             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31761             y : minY
31762         });
31763         
31764         Roo.each(eItems, function(b,k){
31765             
31766             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31767
31768         }, this);
31769         
31770     },
31771     
31772     getVerticalOneBoxColPositions : function(x, y, box)
31773     {
31774         var pos = [];
31775         
31776         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31777         
31778         if(box[0].size == 'md-left'){
31779             rand = 0;
31780         }
31781         
31782         if(box[0].size == 'md-right'){
31783             rand = 1;
31784         }
31785         
31786         pos.push({
31787             x : x + (this.unitWidth + this.gutter) * rand,
31788             y : y
31789         });
31790         
31791         return pos;
31792     },
31793     
31794     getVerticalTwoBoxColPositions : function(x, y, box)
31795     {
31796         var pos = [];
31797         
31798         if(box[0].size == 'xs'){
31799             
31800             pos.push({
31801                 x : x,
31802                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31803             });
31804
31805             pos.push({
31806                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31807                 y : y
31808             });
31809             
31810             return pos;
31811             
31812         }
31813         
31814         pos.push({
31815             x : x,
31816             y : y
31817         });
31818
31819         pos.push({
31820             x : x + (this.unitWidth + this.gutter) * 2,
31821             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31822         });
31823         
31824         return pos;
31825         
31826     },
31827     
31828     getVerticalThreeBoxColPositions : function(x, y, box)
31829     {
31830         var pos = [];
31831         
31832         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31833             
31834             pos.push({
31835                 x : x,
31836                 y : y
31837             });
31838
31839             pos.push({
31840                 x : x + (this.unitWidth + this.gutter) * 1,
31841                 y : y
31842             });
31843             
31844             pos.push({
31845                 x : x + (this.unitWidth + this.gutter) * 2,
31846                 y : y
31847             });
31848             
31849             return pos;
31850             
31851         }
31852         
31853         if(box[0].size == 'xs' && box[1].size == 'xs'){
31854             
31855             pos.push({
31856                 x : x,
31857                 y : y
31858             });
31859
31860             pos.push({
31861                 x : x,
31862                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31863             });
31864             
31865             pos.push({
31866                 x : x + (this.unitWidth + this.gutter) * 1,
31867                 y : y
31868             });
31869             
31870             return pos;
31871             
31872         }
31873         
31874         pos.push({
31875             x : x,
31876             y : y
31877         });
31878
31879         pos.push({
31880             x : x + (this.unitWidth + this.gutter) * 2,
31881             y : y
31882         });
31883
31884         pos.push({
31885             x : x + (this.unitWidth + this.gutter) * 2,
31886             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31887         });
31888             
31889         return pos;
31890         
31891     },
31892     
31893     getVerticalFourBoxColPositions : function(x, y, box)
31894     {
31895         var pos = [];
31896         
31897         if(box[0].size == 'xs'){
31898             
31899             pos.push({
31900                 x : x,
31901                 y : y
31902             });
31903
31904             pos.push({
31905                 x : x,
31906                 y : y + (this.unitHeight + this.gutter) * 1
31907             });
31908             
31909             pos.push({
31910                 x : x,
31911                 y : y + (this.unitHeight + this.gutter) * 2
31912             });
31913             
31914             pos.push({
31915                 x : x + (this.unitWidth + this.gutter) * 1,
31916                 y : y
31917             });
31918             
31919             return pos;
31920             
31921         }
31922         
31923         pos.push({
31924             x : x,
31925             y : y
31926         });
31927
31928         pos.push({
31929             x : x + (this.unitWidth + this.gutter) * 2,
31930             y : y
31931         });
31932
31933         pos.push({
31934             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31935             y : y + (this.unitHeight + this.gutter) * 1
31936         });
31937
31938         pos.push({
31939             x : x + (this.unitWidth + this.gutter) * 2,
31940             y : y + (this.unitWidth + this.gutter) * 2
31941         });
31942
31943         return pos;
31944         
31945     },
31946     
31947     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31948     {
31949         var pos = [];
31950         
31951         if(box[0].size == 'md-left'){
31952             pos.push({
31953                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31954                 y : minY
31955             });
31956             
31957             return pos;
31958         }
31959         
31960         if(box[0].size == 'md-right'){
31961             pos.push({
31962                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31963                 y : minY + (this.unitWidth + this.gutter) * 1
31964             });
31965             
31966             return pos;
31967         }
31968         
31969         var rand = Math.floor(Math.random() * (4 - box[0].y));
31970         
31971         pos.push({
31972             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31973             y : minY + (this.unitWidth + this.gutter) * rand
31974         });
31975         
31976         return pos;
31977         
31978     },
31979     
31980     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31981     {
31982         var pos = [];
31983         
31984         if(box[0].size == 'xs'){
31985             
31986             pos.push({
31987                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31988                 y : minY
31989             });
31990
31991             pos.push({
31992                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31993                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31994             });
31995             
31996             return pos;
31997             
31998         }
31999         
32000         pos.push({
32001             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32002             y : minY
32003         });
32004
32005         pos.push({
32006             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32007             y : minY + (this.unitWidth + this.gutter) * 2
32008         });
32009         
32010         return pos;
32011         
32012     },
32013     
32014     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32015     {
32016         var pos = [];
32017         
32018         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32019             
32020             pos.push({
32021                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32022                 y : minY
32023             });
32024
32025             pos.push({
32026                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32027                 y : minY + (this.unitWidth + this.gutter) * 1
32028             });
32029             
32030             pos.push({
32031                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32032                 y : minY + (this.unitWidth + this.gutter) * 2
32033             });
32034             
32035             return pos;
32036             
32037         }
32038         
32039         if(box[0].size == 'xs' && box[1].size == 'xs'){
32040             
32041             pos.push({
32042                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32043                 y : minY
32044             });
32045
32046             pos.push({
32047                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32048                 y : minY
32049             });
32050             
32051             pos.push({
32052                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32053                 y : minY + (this.unitWidth + this.gutter) * 1
32054             });
32055             
32056             return pos;
32057             
32058         }
32059         
32060         pos.push({
32061             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32062             y : minY
32063         });
32064
32065         pos.push({
32066             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32067             y : minY + (this.unitWidth + this.gutter) * 2
32068         });
32069
32070         pos.push({
32071             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32072             y : minY + (this.unitWidth + this.gutter) * 2
32073         });
32074             
32075         return pos;
32076         
32077     },
32078     
32079     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32080     {
32081         var pos = [];
32082         
32083         if(box[0].size == 'xs'){
32084             
32085             pos.push({
32086                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32087                 y : minY
32088             });
32089
32090             pos.push({
32091                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32092                 y : minY
32093             });
32094             
32095             pos.push({
32096                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32097                 y : minY
32098             });
32099             
32100             pos.push({
32101                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32102                 y : minY + (this.unitWidth + this.gutter) * 1
32103             });
32104             
32105             return pos;
32106             
32107         }
32108         
32109         pos.push({
32110             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32111             y : minY
32112         });
32113         
32114         pos.push({
32115             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32116             y : minY + (this.unitWidth + this.gutter) * 2
32117         });
32118         
32119         pos.push({
32120             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32121             y : minY + (this.unitWidth + this.gutter) * 2
32122         });
32123         
32124         pos.push({
32125             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32126             y : minY + (this.unitWidth + this.gutter) * 2
32127         });
32128
32129         return pos;
32130         
32131     },
32132     
32133     /**
32134     * remove a Masonry Brick
32135     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32136     */
32137     removeBrick : function(brick_id)
32138     {
32139         if (!brick_id) {
32140             return;
32141         }
32142         
32143         for (var i = 0; i<this.bricks.length; i++) {
32144             if (this.bricks[i].id == brick_id) {
32145                 this.bricks.splice(i,1);
32146                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32147                 this.initial();
32148             }
32149         }
32150     },
32151     
32152     /**
32153     * adds a Masonry Brick
32154     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32155     */
32156     addBrick : function(cfg)
32157     {
32158         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32159         //this.register(cn);
32160         cn.parentId = this.id;
32161         cn.render(this.el);
32162         return cn;
32163     },
32164     
32165     /**
32166     * register a Masonry Brick
32167     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32168     */
32169     
32170     register : function(brick)
32171     {
32172         this.bricks.push(brick);
32173         brick.masonryId = this.id;
32174     },
32175     
32176     /**
32177     * clear all the Masonry Brick
32178     */
32179     clearAll : function()
32180     {
32181         this.bricks = [];
32182         //this.getChildContainer().dom.innerHTML = "";
32183         this.el.dom.innerHTML = '';
32184     },
32185     
32186     getSelected : function()
32187     {
32188         if (!this.selectedBrick) {
32189             return false;
32190         }
32191         
32192         return this.selectedBrick;
32193     }
32194 });
32195
32196 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32197     
32198     groups: {},
32199      /**
32200     * register a Masonry Layout
32201     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32202     */
32203     
32204     register : function(layout)
32205     {
32206         this.groups[layout.id] = layout;
32207     },
32208     /**
32209     * fetch a  Masonry Layout based on the masonry layout ID
32210     * @param {string} the masonry layout to add
32211     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32212     */
32213     
32214     get: function(layout_id) {
32215         if (typeof(this.groups[layout_id]) == 'undefined') {
32216             return false;
32217         }
32218         return this.groups[layout_id] ;
32219     }
32220     
32221     
32222     
32223 });
32224
32225  
32226
32227  /**
32228  *
32229  * This is based on 
32230  * http://masonry.desandro.com
32231  *
32232  * The idea is to render all the bricks based on vertical width...
32233  *
32234  * The original code extends 'outlayer' - we might need to use that....
32235  * 
32236  */
32237
32238
32239 /**
32240  * @class Roo.bootstrap.LayoutMasonryAuto
32241  * @extends Roo.bootstrap.Component
32242  * Bootstrap Layout Masonry class
32243  * 
32244  * @constructor
32245  * Create a new Element
32246  * @param {Object} config The config object
32247  */
32248
32249 Roo.bootstrap.LayoutMasonryAuto = function(config){
32250     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32251 };
32252
32253 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32254     
32255       /**
32256      * @cfg {Boolean} isFitWidth  - resize the width..
32257      */   
32258     isFitWidth : false,  // options..
32259     /**
32260      * @cfg {Boolean} isOriginLeft = left align?
32261      */   
32262     isOriginLeft : true,
32263     /**
32264      * @cfg {Boolean} isOriginTop = top align?
32265      */   
32266     isOriginTop : false,
32267     /**
32268      * @cfg {Boolean} isLayoutInstant = no animation?
32269      */   
32270     isLayoutInstant : false, // needed?
32271     /**
32272      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32273      */   
32274     isResizingContainer : true,
32275     /**
32276      * @cfg {Number} columnWidth  width of the columns 
32277      */   
32278     
32279     columnWidth : 0,
32280     
32281     /**
32282      * @cfg {Number} maxCols maximum number of columns
32283      */   
32284     
32285     maxCols: 0,
32286     /**
32287      * @cfg {Number} padHeight padding below box..
32288      */   
32289     
32290     padHeight : 10, 
32291     
32292     /**
32293      * @cfg {Boolean} isAutoInitial defalut true
32294      */   
32295     
32296     isAutoInitial : true, 
32297     
32298     // private?
32299     gutter : 0,
32300     
32301     containerWidth: 0,
32302     initialColumnWidth : 0,
32303     currentSize : null,
32304     
32305     colYs : null, // array.
32306     maxY : 0,
32307     padWidth: 10,
32308     
32309     
32310     tag: 'div',
32311     cls: '',
32312     bricks: null, //CompositeElement
32313     cols : 0, // array?
32314     // element : null, // wrapped now this.el
32315     _isLayoutInited : null, 
32316     
32317     
32318     getAutoCreate : function(){
32319         
32320         var cfg = {
32321             tag: this.tag,
32322             cls: 'blog-masonary-wrapper ' + this.cls,
32323             cn : {
32324                 cls : 'mas-boxes masonary'
32325             }
32326         };
32327         
32328         return cfg;
32329     },
32330     
32331     getChildContainer: function( )
32332     {
32333         if (this.boxesEl) {
32334             return this.boxesEl;
32335         }
32336         
32337         this.boxesEl = this.el.select('.mas-boxes').first();
32338         
32339         return this.boxesEl;
32340     },
32341     
32342     
32343     initEvents : function()
32344     {
32345         var _this = this;
32346         
32347         if(this.isAutoInitial){
32348             Roo.log('hook children rendered');
32349             this.on('childrenrendered', function() {
32350                 Roo.log('children rendered');
32351                 _this.initial();
32352             } ,this);
32353         }
32354         
32355     },
32356     
32357     initial : function()
32358     {
32359         this.reloadItems();
32360
32361         this.currentSize = this.el.getBox(true);
32362
32363         /// was window resize... - let's see if this works..
32364         Roo.EventManager.onWindowResize(this.resize, this); 
32365
32366         if(!this.isAutoInitial){
32367             this.layout();
32368             return;
32369         }
32370         
32371         this.layout.defer(500,this);
32372     },
32373     
32374     reloadItems: function()
32375     {
32376         this.bricks = this.el.select('.masonry-brick', true);
32377         
32378         this.bricks.each(function(b) {
32379             //Roo.log(b.getSize());
32380             if (!b.attr('originalwidth')) {
32381                 b.attr('originalwidth',  b.getSize().width);
32382             }
32383             
32384         });
32385         
32386         Roo.log(this.bricks.elements.length);
32387     },
32388     
32389     resize : function()
32390     {
32391         Roo.log('resize');
32392         var cs = this.el.getBox(true);
32393         
32394         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32395             Roo.log("no change in with or X");
32396             return;
32397         }
32398         this.currentSize = cs;
32399         this.layout();
32400     },
32401     
32402     layout : function()
32403     {
32404          Roo.log('layout');
32405         this._resetLayout();
32406         //this._manageStamps();
32407       
32408         // don't animate first layout
32409         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32410         this.layoutItems( isInstant );
32411       
32412         // flag for initalized
32413         this._isLayoutInited = true;
32414     },
32415     
32416     layoutItems : function( isInstant )
32417     {
32418         //var items = this._getItemsForLayout( this.items );
32419         // original code supports filtering layout items.. we just ignore it..
32420         
32421         this._layoutItems( this.bricks , isInstant );
32422       
32423         this._postLayout();
32424     },
32425     _layoutItems : function ( items , isInstant)
32426     {
32427        //this.fireEvent( 'layout', this, items );
32428     
32429
32430         if ( !items || !items.elements.length ) {
32431           // no items, emit event with empty array
32432             return;
32433         }
32434
32435         var queue = [];
32436         items.each(function(item) {
32437             Roo.log("layout item");
32438             Roo.log(item);
32439             // get x/y object from method
32440             var position = this._getItemLayoutPosition( item );
32441             // enqueue
32442             position.item = item;
32443             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32444             queue.push( position );
32445         }, this);
32446       
32447         this._processLayoutQueue( queue );
32448     },
32449     /** Sets position of item in DOM
32450     * @param {Element} item
32451     * @param {Number} x - horizontal position
32452     * @param {Number} y - vertical position
32453     * @param {Boolean} isInstant - disables transitions
32454     */
32455     _processLayoutQueue : function( queue )
32456     {
32457         for ( var i=0, len = queue.length; i < len; i++ ) {
32458             var obj = queue[i];
32459             obj.item.position('absolute');
32460             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32461         }
32462     },
32463       
32464     
32465     /**
32466     * Any logic you want to do after each layout,
32467     * i.e. size the container
32468     */
32469     _postLayout : function()
32470     {
32471         this.resizeContainer();
32472     },
32473     
32474     resizeContainer : function()
32475     {
32476         if ( !this.isResizingContainer ) {
32477             return;
32478         }
32479         var size = this._getContainerSize();
32480         if ( size ) {
32481             this.el.setSize(size.width,size.height);
32482             this.boxesEl.setSize(size.width,size.height);
32483         }
32484     },
32485     
32486     
32487     
32488     _resetLayout : function()
32489     {
32490         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32491         this.colWidth = this.el.getWidth();
32492         //this.gutter = this.el.getWidth(); 
32493         
32494         this.measureColumns();
32495
32496         // reset column Y
32497         var i = this.cols;
32498         this.colYs = [];
32499         while (i--) {
32500             this.colYs.push( 0 );
32501         }
32502     
32503         this.maxY = 0;
32504     },
32505
32506     measureColumns : function()
32507     {
32508         this.getContainerWidth();
32509       // if columnWidth is 0, default to outerWidth of first item
32510         if ( !this.columnWidth ) {
32511             var firstItem = this.bricks.first();
32512             Roo.log(firstItem);
32513             this.columnWidth  = this.containerWidth;
32514             if (firstItem && firstItem.attr('originalwidth') ) {
32515                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32516             }
32517             // columnWidth fall back to item of first element
32518             Roo.log("set column width?");
32519                         this.initialColumnWidth = this.columnWidth  ;
32520
32521             // if first elem has no width, default to size of container
32522             
32523         }
32524         
32525         
32526         if (this.initialColumnWidth) {
32527             this.columnWidth = this.initialColumnWidth;
32528         }
32529         
32530         
32531             
32532         // column width is fixed at the top - however if container width get's smaller we should
32533         // reduce it...
32534         
32535         // this bit calcs how man columns..
32536             
32537         var columnWidth = this.columnWidth += this.gutter;
32538       
32539         // calculate columns
32540         var containerWidth = this.containerWidth + this.gutter;
32541         
32542         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32543         // fix rounding errors, typically with gutters
32544         var excess = columnWidth - containerWidth % columnWidth;
32545         
32546         
32547         // if overshoot is less than a pixel, round up, otherwise floor it
32548         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32549         cols = Math[ mathMethod ]( cols );
32550         this.cols = Math.max( cols, 1 );
32551         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32552         
32553          // padding positioning..
32554         var totalColWidth = this.cols * this.columnWidth;
32555         var padavail = this.containerWidth - totalColWidth;
32556         // so for 2 columns - we need 3 'pads'
32557         
32558         var padNeeded = (1+this.cols) * this.padWidth;
32559         
32560         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32561         
32562         this.columnWidth += padExtra
32563         //this.padWidth = Math.floor(padavail /  ( this.cols));
32564         
32565         // adjust colum width so that padding is fixed??
32566         
32567         // we have 3 columns ... total = width * 3
32568         // we have X left over... that should be used by 
32569         
32570         //if (this.expandC) {
32571             
32572         //}
32573         
32574         
32575         
32576     },
32577     
32578     getContainerWidth : function()
32579     {
32580        /* // container is parent if fit width
32581         var container = this.isFitWidth ? this.element.parentNode : this.element;
32582         // check that this.size and size are there
32583         // IE8 triggers resize on body size change, so they might not be
32584         
32585         var size = getSize( container );  //FIXME
32586         this.containerWidth = size && size.innerWidth; //FIXME
32587         */
32588          
32589         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32590         
32591     },
32592     
32593     _getItemLayoutPosition : function( item )  // what is item?
32594     {
32595         // we resize the item to our columnWidth..
32596       
32597         item.setWidth(this.columnWidth);
32598         item.autoBoxAdjust  = false;
32599         
32600         var sz = item.getSize();
32601  
32602         // how many columns does this brick span
32603         var remainder = this.containerWidth % this.columnWidth;
32604         
32605         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32606         // round if off by 1 pixel, otherwise use ceil
32607         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32608         colSpan = Math.min( colSpan, this.cols );
32609         
32610         // normally this should be '1' as we dont' currently allow multi width columns..
32611         
32612         var colGroup = this._getColGroup( colSpan );
32613         // get the minimum Y value from the columns
32614         var minimumY = Math.min.apply( Math, colGroup );
32615         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32616         
32617         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32618          
32619         // position the brick
32620         var position = {
32621             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32622             y: this.currentSize.y + minimumY + this.padHeight
32623         };
32624         
32625         Roo.log(position);
32626         // apply setHeight to necessary columns
32627         var setHeight = minimumY + sz.height + this.padHeight;
32628         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32629         
32630         var setSpan = this.cols + 1 - colGroup.length;
32631         for ( var i = 0; i < setSpan; i++ ) {
32632           this.colYs[ shortColIndex + i ] = setHeight ;
32633         }
32634       
32635         return position;
32636     },
32637     
32638     /**
32639      * @param {Number} colSpan - number of columns the element spans
32640      * @returns {Array} colGroup
32641      */
32642     _getColGroup : function( colSpan )
32643     {
32644         if ( colSpan < 2 ) {
32645           // if brick spans only one column, use all the column Ys
32646           return this.colYs;
32647         }
32648       
32649         var colGroup = [];
32650         // how many different places could this brick fit horizontally
32651         var groupCount = this.cols + 1 - colSpan;
32652         // for each group potential horizontal position
32653         for ( var i = 0; i < groupCount; i++ ) {
32654           // make an array of colY values for that one group
32655           var groupColYs = this.colYs.slice( i, i + colSpan );
32656           // and get the max value of the array
32657           colGroup[i] = Math.max.apply( Math, groupColYs );
32658         }
32659         return colGroup;
32660     },
32661     /*
32662     _manageStamp : function( stamp )
32663     {
32664         var stampSize =  stamp.getSize();
32665         var offset = stamp.getBox();
32666         // get the columns that this stamp affects
32667         var firstX = this.isOriginLeft ? offset.x : offset.right;
32668         var lastX = firstX + stampSize.width;
32669         var firstCol = Math.floor( firstX / this.columnWidth );
32670         firstCol = Math.max( 0, firstCol );
32671         
32672         var lastCol = Math.floor( lastX / this.columnWidth );
32673         // lastCol should not go over if multiple of columnWidth #425
32674         lastCol -= lastX % this.columnWidth ? 0 : 1;
32675         lastCol = Math.min( this.cols - 1, lastCol );
32676         
32677         // set colYs to bottom of the stamp
32678         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32679             stampSize.height;
32680             
32681         for ( var i = firstCol; i <= lastCol; i++ ) {
32682           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32683         }
32684     },
32685     */
32686     
32687     _getContainerSize : function()
32688     {
32689         this.maxY = Math.max.apply( Math, this.colYs );
32690         var size = {
32691             height: this.maxY
32692         };
32693       
32694         if ( this.isFitWidth ) {
32695             size.width = this._getContainerFitWidth();
32696         }
32697       
32698         return size;
32699     },
32700     
32701     _getContainerFitWidth : function()
32702     {
32703         var unusedCols = 0;
32704         // count unused columns
32705         var i = this.cols;
32706         while ( --i ) {
32707           if ( this.colYs[i] !== 0 ) {
32708             break;
32709           }
32710           unusedCols++;
32711         }
32712         // fit container to columns that have been used
32713         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32714     },
32715     
32716     needsResizeLayout : function()
32717     {
32718         var previousWidth = this.containerWidth;
32719         this.getContainerWidth();
32720         return previousWidth !== this.containerWidth;
32721     }
32722  
32723 });
32724
32725  
32726
32727  /*
32728  * - LGPL
32729  *
32730  * element
32731  * 
32732  */
32733
32734 /**
32735  * @class Roo.bootstrap.MasonryBrick
32736  * @extends Roo.bootstrap.Component
32737  * Bootstrap MasonryBrick class
32738  * 
32739  * @constructor
32740  * Create a new MasonryBrick
32741  * @param {Object} config The config object
32742  */
32743
32744 Roo.bootstrap.MasonryBrick = function(config){
32745     
32746     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32747     
32748     Roo.bootstrap.MasonryBrick.register(this);
32749     
32750     this.addEvents({
32751         // raw events
32752         /**
32753          * @event click
32754          * When a MasonryBrick is clcik
32755          * @param {Roo.bootstrap.MasonryBrick} this
32756          * @param {Roo.EventObject} e
32757          */
32758         "click" : true
32759     });
32760 };
32761
32762 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32763     
32764     /**
32765      * @cfg {String} title
32766      */   
32767     title : '',
32768     /**
32769      * @cfg {String} html
32770      */   
32771     html : '',
32772     /**
32773      * @cfg {String} bgimage
32774      */   
32775     bgimage : '',
32776     /**
32777      * @cfg {String} videourl
32778      */   
32779     videourl : '',
32780     /**
32781      * @cfg {String} cls
32782      */   
32783     cls : '',
32784     /**
32785      * @cfg {String} href
32786      */   
32787     href : '',
32788     /**
32789      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32790      */   
32791     size : 'xs',
32792     
32793     /**
32794      * @cfg {String} placetitle (center|bottom)
32795      */   
32796     placetitle : '',
32797     
32798     /**
32799      * @cfg {Boolean} isFitContainer defalut true
32800      */   
32801     isFitContainer : true, 
32802     
32803     /**
32804      * @cfg {Boolean} preventDefault defalut false
32805      */   
32806     preventDefault : false, 
32807     
32808     /**
32809      * @cfg {Boolean} inverse defalut false
32810      */   
32811     maskInverse : false, 
32812     
32813     getAutoCreate : function()
32814     {
32815         if(!this.isFitContainer){
32816             return this.getSplitAutoCreate();
32817         }
32818         
32819         var cls = 'masonry-brick masonry-brick-full';
32820         
32821         if(this.href.length){
32822             cls += ' masonry-brick-link';
32823         }
32824         
32825         if(this.bgimage.length){
32826             cls += ' masonry-brick-image';
32827         }
32828         
32829         if(this.maskInverse){
32830             cls += ' mask-inverse';
32831         }
32832         
32833         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32834             cls += ' enable-mask';
32835         }
32836         
32837         if(this.size){
32838             cls += ' masonry-' + this.size + '-brick';
32839         }
32840         
32841         if(this.placetitle.length){
32842             
32843             switch (this.placetitle) {
32844                 case 'center' :
32845                     cls += ' masonry-center-title';
32846                     break;
32847                 case 'bottom' :
32848                     cls += ' masonry-bottom-title';
32849                     break;
32850                 default:
32851                     break;
32852             }
32853             
32854         } else {
32855             if(!this.html.length && !this.bgimage.length){
32856                 cls += ' masonry-center-title';
32857             }
32858
32859             if(!this.html.length && this.bgimage.length){
32860                 cls += ' masonry-bottom-title';
32861             }
32862         }
32863         
32864         if(this.cls){
32865             cls += ' ' + this.cls;
32866         }
32867         
32868         var cfg = {
32869             tag: (this.href.length) ? 'a' : 'div',
32870             cls: cls,
32871             cn: [
32872                 {
32873                     tag: 'div',
32874                     cls: 'masonry-brick-mask'
32875                 },
32876                 {
32877                     tag: 'div',
32878                     cls: 'masonry-brick-paragraph',
32879                     cn: []
32880                 }
32881             ]
32882         };
32883         
32884         if(this.href.length){
32885             cfg.href = this.href;
32886         }
32887         
32888         var cn = cfg.cn[1].cn;
32889         
32890         if(this.title.length){
32891             cn.push({
32892                 tag: 'h4',
32893                 cls: 'masonry-brick-title',
32894                 html: this.title
32895             });
32896         }
32897         
32898         if(this.html.length){
32899             cn.push({
32900                 tag: 'p',
32901                 cls: 'masonry-brick-text',
32902                 html: this.html
32903             });
32904         }
32905         
32906         if (!this.title.length && !this.html.length) {
32907             cfg.cn[1].cls += ' hide';
32908         }
32909         
32910         if(this.bgimage.length){
32911             cfg.cn.push({
32912                 tag: 'img',
32913                 cls: 'masonry-brick-image-view',
32914                 src: this.bgimage
32915             });
32916         }
32917         
32918         if(this.videourl.length){
32919             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32920             // youtube support only?
32921             cfg.cn.push({
32922                 tag: 'iframe',
32923                 cls: 'masonry-brick-image-view',
32924                 src: vurl,
32925                 frameborder : 0,
32926                 allowfullscreen : true
32927             });
32928         }
32929         
32930         return cfg;
32931         
32932     },
32933     
32934     getSplitAutoCreate : function()
32935     {
32936         var cls = 'masonry-brick masonry-brick-split';
32937         
32938         if(this.href.length){
32939             cls += ' masonry-brick-link';
32940         }
32941         
32942         if(this.bgimage.length){
32943             cls += ' masonry-brick-image';
32944         }
32945         
32946         if(this.size){
32947             cls += ' masonry-' + this.size + '-brick';
32948         }
32949         
32950         switch (this.placetitle) {
32951             case 'center' :
32952                 cls += ' masonry-center-title';
32953                 break;
32954             case 'bottom' :
32955                 cls += ' masonry-bottom-title';
32956                 break;
32957             default:
32958                 if(!this.bgimage.length){
32959                     cls += ' masonry-center-title';
32960                 }
32961
32962                 if(this.bgimage.length){
32963                     cls += ' masonry-bottom-title';
32964                 }
32965                 break;
32966         }
32967         
32968         if(this.cls){
32969             cls += ' ' + this.cls;
32970         }
32971         
32972         var cfg = {
32973             tag: (this.href.length) ? 'a' : 'div',
32974             cls: cls,
32975             cn: [
32976                 {
32977                     tag: 'div',
32978                     cls: 'masonry-brick-split-head',
32979                     cn: [
32980                         {
32981                             tag: 'div',
32982                             cls: 'masonry-brick-paragraph',
32983                             cn: []
32984                         }
32985                     ]
32986                 },
32987                 {
32988                     tag: 'div',
32989                     cls: 'masonry-brick-split-body',
32990                     cn: []
32991                 }
32992             ]
32993         };
32994         
32995         if(this.href.length){
32996             cfg.href = this.href;
32997         }
32998         
32999         if(this.title.length){
33000             cfg.cn[0].cn[0].cn.push({
33001                 tag: 'h4',
33002                 cls: 'masonry-brick-title',
33003                 html: this.title
33004             });
33005         }
33006         
33007         if(this.html.length){
33008             cfg.cn[1].cn.push({
33009                 tag: 'p',
33010                 cls: 'masonry-brick-text',
33011                 html: this.html
33012             });
33013         }
33014
33015         if(this.bgimage.length){
33016             cfg.cn[0].cn.push({
33017                 tag: 'img',
33018                 cls: 'masonry-brick-image-view',
33019                 src: this.bgimage
33020             });
33021         }
33022         
33023         if(this.videourl.length){
33024             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33025             // youtube support only?
33026             cfg.cn[0].cn.cn.push({
33027                 tag: 'iframe',
33028                 cls: 'masonry-brick-image-view',
33029                 src: vurl,
33030                 frameborder : 0,
33031                 allowfullscreen : true
33032             });
33033         }
33034         
33035         return cfg;
33036     },
33037     
33038     initEvents: function() 
33039     {
33040         switch (this.size) {
33041             case 'xs' :
33042                 this.x = 1;
33043                 this.y = 1;
33044                 break;
33045             case 'sm' :
33046                 this.x = 2;
33047                 this.y = 2;
33048                 break;
33049             case 'md' :
33050             case 'md-left' :
33051             case 'md-right' :
33052                 this.x = 3;
33053                 this.y = 3;
33054                 break;
33055             case 'tall' :
33056                 this.x = 2;
33057                 this.y = 3;
33058                 break;
33059             case 'wide' :
33060                 this.x = 3;
33061                 this.y = 2;
33062                 break;
33063             case 'wide-thin' :
33064                 this.x = 3;
33065                 this.y = 1;
33066                 break;
33067                         
33068             default :
33069                 break;
33070         }
33071         
33072         if(Roo.isTouch){
33073             this.el.on('touchstart', this.onTouchStart, this);
33074             this.el.on('touchmove', this.onTouchMove, this);
33075             this.el.on('touchend', this.onTouchEnd, this);
33076             this.el.on('contextmenu', this.onContextMenu, this);
33077         } else {
33078             this.el.on('mouseenter'  ,this.enter, this);
33079             this.el.on('mouseleave', this.leave, this);
33080             this.el.on('click', this.onClick, this);
33081         }
33082         
33083         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33084             this.parent().bricks.push(this);   
33085         }
33086         
33087     },
33088     
33089     onClick: function(e, el)
33090     {
33091         var time = this.endTimer - this.startTimer;
33092         // Roo.log(e.preventDefault());
33093         if(Roo.isTouch){
33094             if(time > 1000){
33095                 e.preventDefault();
33096                 return;
33097             }
33098         }
33099         
33100         if(!this.preventDefault){
33101             return;
33102         }
33103         
33104         e.preventDefault();
33105         
33106         if (this.activeClass != '') {
33107             this.selectBrick();
33108         }
33109         
33110         this.fireEvent('click', this, e);
33111     },
33112     
33113     enter: function(e, el)
33114     {
33115         e.preventDefault();
33116         
33117         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33118             return;
33119         }
33120         
33121         if(this.bgimage.length && this.html.length){
33122             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33123         }
33124     },
33125     
33126     leave: function(e, el)
33127     {
33128         e.preventDefault();
33129         
33130         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33131             return;
33132         }
33133         
33134         if(this.bgimage.length && this.html.length){
33135             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33136         }
33137     },
33138     
33139     onTouchStart: function(e, el)
33140     {
33141 //        e.preventDefault();
33142         
33143         this.touchmoved = false;
33144         
33145         if(!this.isFitContainer){
33146             return;
33147         }
33148         
33149         if(!this.bgimage.length || !this.html.length){
33150             return;
33151         }
33152         
33153         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33154         
33155         this.timer = new Date().getTime();
33156         
33157     },
33158     
33159     onTouchMove: function(e, el)
33160     {
33161         this.touchmoved = true;
33162     },
33163     
33164     onContextMenu : function(e,el)
33165     {
33166         e.preventDefault();
33167         e.stopPropagation();
33168         return false;
33169     },
33170     
33171     onTouchEnd: function(e, el)
33172     {
33173 //        e.preventDefault();
33174         
33175         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33176         
33177             this.leave(e,el);
33178             
33179             return;
33180         }
33181         
33182         if(!this.bgimage.length || !this.html.length){
33183             
33184             if(this.href.length){
33185                 window.location.href = this.href;
33186             }
33187             
33188             return;
33189         }
33190         
33191         if(!this.isFitContainer){
33192             return;
33193         }
33194         
33195         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33196         
33197         window.location.href = this.href;
33198     },
33199     
33200     //selection on single brick only
33201     selectBrick : function() {
33202         
33203         if (!this.parentId) {
33204             return;
33205         }
33206         
33207         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33208         var index = m.selectedBrick.indexOf(this.id);
33209         
33210         if ( index > -1) {
33211             m.selectedBrick.splice(index,1);
33212             this.el.removeClass(this.activeClass);
33213             return;
33214         }
33215         
33216         for(var i = 0; i < m.selectedBrick.length; i++) {
33217             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33218             b.el.removeClass(b.activeClass);
33219         }
33220         
33221         m.selectedBrick = [];
33222         
33223         m.selectedBrick.push(this.id);
33224         this.el.addClass(this.activeClass);
33225         return;
33226     },
33227     
33228     isSelected : function(){
33229         return this.el.hasClass(this.activeClass);
33230         
33231     }
33232 });
33233
33234 Roo.apply(Roo.bootstrap.MasonryBrick, {
33235     
33236     //groups: {},
33237     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33238      /**
33239     * register a Masonry Brick
33240     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33241     */
33242     
33243     register : function(brick)
33244     {
33245         //this.groups[brick.id] = brick;
33246         this.groups.add(brick.id, brick);
33247     },
33248     /**
33249     * fetch a  masonry brick based on the masonry brick ID
33250     * @param {string} the masonry brick to add
33251     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33252     */
33253     
33254     get: function(brick_id) 
33255     {
33256         // if (typeof(this.groups[brick_id]) == 'undefined') {
33257         //     return false;
33258         // }
33259         // return this.groups[brick_id] ;
33260         
33261         if(this.groups.key(brick_id)) {
33262             return this.groups.key(brick_id);
33263         }
33264         
33265         return false;
33266     }
33267     
33268     
33269     
33270 });
33271
33272  /*
33273  * - LGPL
33274  *
33275  * element
33276  * 
33277  */
33278
33279 /**
33280  * @class Roo.bootstrap.Brick
33281  * @extends Roo.bootstrap.Component
33282  * Bootstrap Brick class
33283  * 
33284  * @constructor
33285  * Create a new Brick
33286  * @param {Object} config The config object
33287  */
33288
33289 Roo.bootstrap.Brick = function(config){
33290     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33291     
33292     this.addEvents({
33293         // raw events
33294         /**
33295          * @event click
33296          * When a Brick is click
33297          * @param {Roo.bootstrap.Brick} this
33298          * @param {Roo.EventObject} e
33299          */
33300         "click" : true
33301     });
33302 };
33303
33304 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33305     
33306     /**
33307      * @cfg {String} title
33308      */   
33309     title : '',
33310     /**
33311      * @cfg {String} html
33312      */   
33313     html : '',
33314     /**
33315      * @cfg {String} bgimage
33316      */   
33317     bgimage : '',
33318     /**
33319      * @cfg {String} cls
33320      */   
33321     cls : '',
33322     /**
33323      * @cfg {String} href
33324      */   
33325     href : '',
33326     /**
33327      * @cfg {String} video
33328      */   
33329     video : '',
33330     /**
33331      * @cfg {Boolean} square
33332      */   
33333     square : true,
33334     
33335     getAutoCreate : function()
33336     {
33337         var cls = 'roo-brick';
33338         
33339         if(this.href.length){
33340             cls += ' roo-brick-link';
33341         }
33342         
33343         if(this.bgimage.length){
33344             cls += ' roo-brick-image';
33345         }
33346         
33347         if(!this.html.length && !this.bgimage.length){
33348             cls += ' roo-brick-center-title';
33349         }
33350         
33351         if(!this.html.length && this.bgimage.length){
33352             cls += ' roo-brick-bottom-title';
33353         }
33354         
33355         if(this.cls){
33356             cls += ' ' + this.cls;
33357         }
33358         
33359         var cfg = {
33360             tag: (this.href.length) ? 'a' : 'div',
33361             cls: cls,
33362             cn: [
33363                 {
33364                     tag: 'div',
33365                     cls: 'roo-brick-paragraph',
33366                     cn: []
33367                 }
33368             ]
33369         };
33370         
33371         if(this.href.length){
33372             cfg.href = this.href;
33373         }
33374         
33375         var cn = cfg.cn[0].cn;
33376         
33377         if(this.title.length){
33378             cn.push({
33379                 tag: 'h4',
33380                 cls: 'roo-brick-title',
33381                 html: this.title
33382             });
33383         }
33384         
33385         if(this.html.length){
33386             cn.push({
33387                 tag: 'p',
33388                 cls: 'roo-brick-text',
33389                 html: this.html
33390             });
33391         } else {
33392             cn.cls += ' hide';
33393         }
33394         
33395         if(this.bgimage.length){
33396             cfg.cn.push({
33397                 tag: 'img',
33398                 cls: 'roo-brick-image-view',
33399                 src: this.bgimage
33400             });
33401         }
33402         
33403         return cfg;
33404     },
33405     
33406     initEvents: function() 
33407     {
33408         if(this.title.length || this.html.length){
33409             this.el.on('mouseenter'  ,this.enter, this);
33410             this.el.on('mouseleave', this.leave, this);
33411         }
33412         
33413         Roo.EventManager.onWindowResize(this.resize, this); 
33414         
33415         if(this.bgimage.length){
33416             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33417             this.imageEl.on('load', this.onImageLoad, this);
33418             return;
33419         }
33420         
33421         this.resize();
33422     },
33423     
33424     onImageLoad : function()
33425     {
33426         this.resize();
33427     },
33428     
33429     resize : function()
33430     {
33431         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33432         
33433         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33434         
33435         if(this.bgimage.length){
33436             var image = this.el.select('.roo-brick-image-view', true).first();
33437             
33438             image.setWidth(paragraph.getWidth());
33439             
33440             if(this.square){
33441                 image.setHeight(paragraph.getWidth());
33442             }
33443             
33444             this.el.setHeight(image.getHeight());
33445             paragraph.setHeight(image.getHeight());
33446             
33447         }
33448         
33449     },
33450     
33451     enter: function(e, el)
33452     {
33453         e.preventDefault();
33454         
33455         if(this.bgimage.length){
33456             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33457             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33458         }
33459     },
33460     
33461     leave: function(e, el)
33462     {
33463         e.preventDefault();
33464         
33465         if(this.bgimage.length){
33466             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33467             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33468         }
33469     }
33470     
33471 });
33472
33473  
33474
33475  /*
33476  * - LGPL
33477  *
33478  * Number field 
33479  */
33480
33481 /**
33482  * @class Roo.bootstrap.NumberField
33483  * @extends Roo.bootstrap.Input
33484  * Bootstrap NumberField class
33485  * 
33486  * 
33487  * 
33488  * 
33489  * @constructor
33490  * Create a new NumberField
33491  * @param {Object} config The config object
33492  */
33493
33494 Roo.bootstrap.NumberField = function(config){
33495     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33496 };
33497
33498 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33499     
33500     /**
33501      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33502      */
33503     allowDecimals : true,
33504     /**
33505      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33506      */
33507     decimalSeparator : ".",
33508     /**
33509      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33510      */
33511     decimalPrecision : 2,
33512     /**
33513      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33514      */
33515     allowNegative : true,
33516     
33517     /**
33518      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33519      */
33520     allowZero: true,
33521     /**
33522      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33523      */
33524     minValue : Number.NEGATIVE_INFINITY,
33525     /**
33526      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33527      */
33528     maxValue : Number.MAX_VALUE,
33529     /**
33530      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33531      */
33532     minText : "The minimum value for this field is {0}",
33533     /**
33534      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33535      */
33536     maxText : "The maximum value for this field is {0}",
33537     /**
33538      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33539      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33540      */
33541     nanText : "{0} is not a valid number",
33542     /**
33543      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33544      */
33545     thousandsDelimiter : false,
33546     /**
33547      * @cfg {String} valueAlign alignment of value
33548      */
33549     valueAlign : "left",
33550
33551     getAutoCreate : function()
33552     {
33553         var hiddenInput = {
33554             tag: 'input',
33555             type: 'hidden',
33556             id: Roo.id(),
33557             cls: 'hidden-number-input'
33558         };
33559         
33560         if (this.name) {
33561             hiddenInput.name = this.name;
33562         }
33563         
33564         this.name = '';
33565         
33566         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33567         
33568         this.name = hiddenInput.name;
33569         
33570         if(cfg.cn.length > 0) {
33571             cfg.cn.push(hiddenInput);
33572         }
33573         
33574         return cfg;
33575     },
33576
33577     // private
33578     initEvents : function()
33579     {   
33580         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33581         
33582         var allowed = "0123456789";
33583         
33584         if(this.allowDecimals){
33585             allowed += this.decimalSeparator;
33586         }
33587         
33588         if(this.allowNegative){
33589             allowed += "-";
33590         }
33591         
33592         if(this.thousandsDelimiter) {
33593             allowed += ",";
33594         }
33595         
33596         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33597         
33598         var keyPress = function(e){
33599             
33600             var k = e.getKey();
33601             
33602             var c = e.getCharCode();
33603             
33604             if(
33605                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33606                     allowed.indexOf(String.fromCharCode(c)) === -1
33607             ){
33608                 e.stopEvent();
33609                 return;
33610             }
33611             
33612             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33613                 return;
33614             }
33615             
33616             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33617                 e.stopEvent();
33618             }
33619         };
33620         
33621         this.el.on("keypress", keyPress, this);
33622     },
33623     
33624     validateValue : function(value)
33625     {
33626         
33627         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33628             return false;
33629         }
33630         
33631         var num = this.parseValue(value);
33632         
33633         if(isNaN(num)){
33634             this.markInvalid(String.format(this.nanText, value));
33635             return false;
33636         }
33637         
33638         if(num < this.minValue){
33639             this.markInvalid(String.format(this.minText, this.minValue));
33640             return false;
33641         }
33642         
33643         if(num > this.maxValue){
33644             this.markInvalid(String.format(this.maxText, this.maxValue));
33645             return false;
33646         }
33647         
33648         return true;
33649     },
33650
33651     getValue : function()
33652     {
33653         var v = this.hiddenEl().getValue();
33654         
33655         return this.fixPrecision(this.parseValue(v));
33656     },
33657
33658     parseValue : function(value)
33659     {
33660         if(this.thousandsDelimiter) {
33661             value += "";
33662             r = new RegExp(",", "g");
33663             value = value.replace(r, "");
33664         }
33665         
33666         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33667         return isNaN(value) ? '' : value;
33668     },
33669
33670     fixPrecision : function(value)
33671     {
33672         if(this.thousandsDelimiter) {
33673             value += "";
33674             r = new RegExp(",", "g");
33675             value = value.replace(r, "");
33676         }
33677         
33678         var nan = isNaN(value);
33679         
33680         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33681             return nan ? '' : value;
33682         }
33683         return parseFloat(value).toFixed(this.decimalPrecision);
33684     },
33685
33686     setValue : function(v)
33687     {
33688         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33689         
33690         this.value = v;
33691         
33692         if(this.rendered){
33693             
33694             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33695             
33696             this.inputEl().dom.value = (v == '') ? '' :
33697                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33698             
33699             if(!this.allowZero && v === '0') {
33700                 this.hiddenEl().dom.value = '';
33701                 this.inputEl().dom.value = '';
33702             }
33703             
33704             this.validate();
33705         }
33706     },
33707
33708     decimalPrecisionFcn : function(v)
33709     {
33710         return Math.floor(v);
33711     },
33712
33713     beforeBlur : function()
33714     {
33715         var v = this.parseValue(this.getRawValue());
33716         
33717         if(v || v === 0 || v === ''){
33718             this.setValue(v);
33719         }
33720     },
33721     
33722     hiddenEl : function()
33723     {
33724         return this.el.select('input.hidden-number-input',true).first();
33725     }
33726     
33727 });
33728
33729  
33730
33731 /*
33732 * Licence: LGPL
33733 */
33734
33735 /**
33736  * @class Roo.bootstrap.DocumentSlider
33737  * @extends Roo.bootstrap.Component
33738  * Bootstrap DocumentSlider class
33739  * 
33740  * @constructor
33741  * Create a new DocumentViewer
33742  * @param {Object} config The config object
33743  */
33744
33745 Roo.bootstrap.DocumentSlider = function(config){
33746     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33747     
33748     this.files = [];
33749     
33750     this.addEvents({
33751         /**
33752          * @event initial
33753          * Fire after initEvent
33754          * @param {Roo.bootstrap.DocumentSlider} this
33755          */
33756         "initial" : true,
33757         /**
33758          * @event update
33759          * Fire after update
33760          * @param {Roo.bootstrap.DocumentSlider} this
33761          */
33762         "update" : true,
33763         /**
33764          * @event click
33765          * Fire after click
33766          * @param {Roo.bootstrap.DocumentSlider} this
33767          */
33768         "click" : true
33769     });
33770 };
33771
33772 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33773     
33774     files : false,
33775     
33776     indicator : 0,
33777     
33778     getAutoCreate : function()
33779     {
33780         var cfg = {
33781             tag : 'div',
33782             cls : 'roo-document-slider',
33783             cn : [
33784                 {
33785                     tag : 'div',
33786                     cls : 'roo-document-slider-header',
33787                     cn : [
33788                         {
33789                             tag : 'div',
33790                             cls : 'roo-document-slider-header-title'
33791                         }
33792                     ]
33793                 },
33794                 {
33795                     tag : 'div',
33796                     cls : 'roo-document-slider-body',
33797                     cn : [
33798                         {
33799                             tag : 'div',
33800                             cls : 'roo-document-slider-prev',
33801                             cn : [
33802                                 {
33803                                     tag : 'i',
33804                                     cls : 'fa fa-chevron-left'
33805                                 }
33806                             ]
33807                         },
33808                         {
33809                             tag : 'div',
33810                             cls : 'roo-document-slider-thumb',
33811                             cn : [
33812                                 {
33813                                     tag : 'img',
33814                                     cls : 'roo-document-slider-image'
33815                                 }
33816                             ]
33817                         },
33818                         {
33819                             tag : 'div',
33820                             cls : 'roo-document-slider-next',
33821                             cn : [
33822                                 {
33823                                     tag : 'i',
33824                                     cls : 'fa fa-chevron-right'
33825                                 }
33826                             ]
33827                         }
33828                     ]
33829                 }
33830             ]
33831         };
33832         
33833         return cfg;
33834     },
33835     
33836     initEvents : function()
33837     {
33838         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33839         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33840         
33841         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33842         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33843         
33844         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33845         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33846         
33847         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33848         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33849         
33850         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33851         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33852         
33853         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33854         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33855         
33856         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33857         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33858         
33859         this.thumbEl.on('click', this.onClick, this);
33860         
33861         this.prevIndicator.on('click', this.prev, this);
33862         
33863         this.nextIndicator.on('click', this.next, this);
33864         
33865     },
33866     
33867     initial : function()
33868     {
33869         if(this.files.length){
33870             this.indicator = 1;
33871             this.update()
33872         }
33873         
33874         this.fireEvent('initial', this);
33875     },
33876     
33877     update : function()
33878     {
33879         this.imageEl.attr('src', this.files[this.indicator - 1]);
33880         
33881         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33882         
33883         this.prevIndicator.show();
33884         
33885         if(this.indicator == 1){
33886             this.prevIndicator.hide();
33887         }
33888         
33889         this.nextIndicator.show();
33890         
33891         if(this.indicator == this.files.length){
33892             this.nextIndicator.hide();
33893         }
33894         
33895         this.thumbEl.scrollTo('top');
33896         
33897         this.fireEvent('update', this);
33898     },
33899     
33900     onClick : function(e)
33901     {
33902         e.preventDefault();
33903         
33904         this.fireEvent('click', this);
33905     },
33906     
33907     prev : function(e)
33908     {
33909         e.preventDefault();
33910         
33911         this.indicator = Math.max(1, this.indicator - 1);
33912         
33913         this.update();
33914     },
33915     
33916     next : function(e)
33917     {
33918         e.preventDefault();
33919         
33920         this.indicator = Math.min(this.files.length, this.indicator + 1);
33921         
33922         this.update();
33923     }
33924 });
33925 /*
33926  * - LGPL
33927  *
33928  * RadioSet
33929  *
33930  *
33931  */
33932
33933 /**
33934  * @class Roo.bootstrap.RadioSet
33935  * @extends Roo.bootstrap.Input
33936  * Bootstrap RadioSet class
33937  * @cfg {String} indicatorpos (left|right) default left
33938  * @cfg {Boolean} inline (true|false) inline the element (default true)
33939  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33940  * @constructor
33941  * Create a new RadioSet
33942  * @param {Object} config The config object
33943  */
33944
33945 Roo.bootstrap.RadioSet = function(config){
33946     
33947     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33948     
33949     this.radioes = [];
33950     
33951     Roo.bootstrap.RadioSet.register(this);
33952     
33953     this.addEvents({
33954         /**
33955         * @event check
33956         * Fires when the element is checked or unchecked.
33957         * @param {Roo.bootstrap.RadioSet} this This radio
33958         * @param {Roo.bootstrap.Radio} item The checked item
33959         */
33960        check : true,
33961        /**
33962         * @event click
33963         * Fires when the element is click.
33964         * @param {Roo.bootstrap.RadioSet} this This radio set
33965         * @param {Roo.bootstrap.Radio} item The checked item
33966         * @param {Roo.EventObject} e The event object
33967         */
33968        click : true
33969     });
33970     
33971 };
33972
33973 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33974
33975     radioes : false,
33976     
33977     inline : true,
33978     
33979     weight : '',
33980     
33981     indicatorpos : 'left',
33982     
33983     getAutoCreate : function()
33984     {
33985         var label = {
33986             tag : 'label',
33987             cls : 'roo-radio-set-label',
33988             cn : [
33989                 {
33990                     tag : 'span',
33991                     html : this.fieldLabel
33992                 }
33993             ]
33994         };
33995         
33996         if(this.indicatorpos == 'left'){
33997             label.cn.unshift({
33998                 tag : 'i',
33999                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34000                 tooltip : 'This field is required'
34001             });
34002         } else {
34003             label.cn.push({
34004                 tag : 'i',
34005                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34006                 tooltip : 'This field is required'
34007             });
34008         }
34009         
34010         var items = {
34011             tag : 'div',
34012             cls : 'roo-radio-set-items'
34013         };
34014         
34015         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34016         
34017         if (align === 'left' && this.fieldLabel.length) {
34018             
34019             items = {
34020                 cls : "roo-radio-set-right", 
34021                 cn: [
34022                     items
34023                 ]
34024             };
34025             
34026             if(this.labelWidth > 12){
34027                 label.style = "width: " + this.labelWidth + 'px';
34028             }
34029             
34030             if(this.labelWidth < 13 && this.labelmd == 0){
34031                 this.labelmd = this.labelWidth;
34032             }
34033             
34034             if(this.labellg > 0){
34035                 label.cls += ' col-lg-' + this.labellg;
34036                 items.cls += ' col-lg-' + (12 - this.labellg);
34037             }
34038             
34039             if(this.labelmd > 0){
34040                 label.cls += ' col-md-' + this.labelmd;
34041                 items.cls += ' col-md-' + (12 - this.labelmd);
34042             }
34043             
34044             if(this.labelsm > 0){
34045                 label.cls += ' col-sm-' + this.labelsm;
34046                 items.cls += ' col-sm-' + (12 - this.labelsm);
34047             }
34048             
34049             if(this.labelxs > 0){
34050                 label.cls += ' col-xs-' + this.labelxs;
34051                 items.cls += ' col-xs-' + (12 - this.labelxs);
34052             }
34053         }
34054         
34055         var cfg = {
34056             tag : 'div',
34057             cls : 'roo-radio-set',
34058             cn : [
34059                 {
34060                     tag : 'input',
34061                     cls : 'roo-radio-set-input',
34062                     type : 'hidden',
34063                     name : this.name,
34064                     value : this.value ? this.value :  ''
34065                 },
34066                 label,
34067                 items
34068             ]
34069         };
34070         
34071         if(this.weight.length){
34072             cfg.cls += ' roo-radio-' + this.weight;
34073         }
34074         
34075         if(this.inline) {
34076             cfg.cls += ' roo-radio-set-inline';
34077         }
34078         
34079         var settings=this;
34080         ['xs','sm','md','lg'].map(function(size){
34081             if (settings[size]) {
34082                 cfg.cls += ' col-' + size + '-' + settings[size];
34083             }
34084         });
34085         
34086         return cfg;
34087         
34088     },
34089
34090     initEvents : function()
34091     {
34092         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34093         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34094         
34095         if(!this.fieldLabel.length){
34096             this.labelEl.hide();
34097         }
34098         
34099         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34100         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34101         
34102         this.indicator = this.indicatorEl();
34103         
34104         if(this.indicator){
34105             this.indicator.addClass('invisible');
34106         }
34107         
34108         this.originalValue = this.getValue();
34109         
34110     },
34111     
34112     inputEl: function ()
34113     {
34114         return this.el.select('.roo-radio-set-input', true).first();
34115     },
34116     
34117     getChildContainer : function()
34118     {
34119         return this.itemsEl;
34120     },
34121     
34122     register : function(item)
34123     {
34124         this.radioes.push(item);
34125         
34126     },
34127     
34128     validate : function()
34129     {   
34130         if(this.getVisibilityEl().hasClass('hidden')){
34131             return true;
34132         }
34133         
34134         var valid = false;
34135         
34136         Roo.each(this.radioes, function(i){
34137             if(!i.checked){
34138                 return;
34139             }
34140             
34141             valid = true;
34142             return false;
34143         });
34144         
34145         if(this.allowBlank) {
34146             return true;
34147         }
34148         
34149         if(this.disabled || valid){
34150             this.markValid();
34151             return true;
34152         }
34153         
34154         this.markInvalid();
34155         return false;
34156         
34157     },
34158     
34159     markValid : function()
34160     {
34161         if(this.labelEl.isVisible(true)){
34162             this.indicatorEl().removeClass('visible');
34163             this.indicatorEl().addClass('invisible');
34164         }
34165         
34166         this.el.removeClass([this.invalidClass, this.validClass]);
34167         this.el.addClass(this.validClass);
34168         
34169         this.fireEvent('valid', this);
34170     },
34171     
34172     markInvalid : function(msg)
34173     {
34174         if(this.allowBlank || this.disabled){
34175             return;
34176         }
34177         
34178         if(this.labelEl.isVisible(true)){
34179             this.indicatorEl().removeClass('invisible');
34180             this.indicatorEl().addClass('visible');
34181         }
34182         
34183         this.el.removeClass([this.invalidClass, this.validClass]);
34184         this.el.addClass(this.invalidClass);
34185         
34186         this.fireEvent('invalid', this, msg);
34187         
34188     },
34189     
34190     setValue : function(v, suppressEvent)
34191     {   
34192         if(this.value === v){
34193             return;
34194         }
34195         
34196         this.value = v;
34197         
34198         if(this.rendered){
34199             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34200         }
34201         
34202         Roo.each(this.radioes, function(i){
34203             i.checked = false;
34204             i.el.removeClass('checked');
34205         });
34206         
34207         Roo.each(this.radioes, function(i){
34208             
34209             if(i.value === v || i.value.toString() === v.toString()){
34210                 i.checked = true;
34211                 i.el.addClass('checked');
34212                 
34213                 if(suppressEvent !== true){
34214                     this.fireEvent('check', this, i);
34215                 }
34216                 
34217                 return false;
34218             }
34219             
34220         }, this);
34221         
34222         this.validate();
34223     },
34224     
34225     clearInvalid : function(){
34226         
34227         if(!this.el || this.preventMark){
34228             return;
34229         }
34230         
34231         this.el.removeClass([this.invalidClass]);
34232         
34233         this.fireEvent('valid', this);
34234     }
34235     
34236 });
34237
34238 Roo.apply(Roo.bootstrap.RadioSet, {
34239     
34240     groups: {},
34241     
34242     register : function(set)
34243     {
34244         this.groups[set.name] = set;
34245     },
34246     
34247     get: function(name) 
34248     {
34249         if (typeof(this.groups[name]) == 'undefined') {
34250             return false;
34251         }
34252         
34253         return this.groups[name] ;
34254     }
34255     
34256 });
34257 /*
34258  * Based on:
34259  * Ext JS Library 1.1.1
34260  * Copyright(c) 2006-2007, Ext JS, LLC.
34261  *
34262  * Originally Released Under LGPL - original licence link has changed is not relivant.
34263  *
34264  * Fork - LGPL
34265  * <script type="text/javascript">
34266  */
34267
34268
34269 /**
34270  * @class Roo.bootstrap.SplitBar
34271  * @extends Roo.util.Observable
34272  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34273  * <br><br>
34274  * Usage:
34275  * <pre><code>
34276 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34277                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34278 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34279 split.minSize = 100;
34280 split.maxSize = 600;
34281 split.animate = true;
34282 split.on('moved', splitterMoved);
34283 </code></pre>
34284  * @constructor
34285  * Create a new SplitBar
34286  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34287  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34288  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34289  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34290                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34291                         position of the SplitBar).
34292  */
34293 Roo.bootstrap.SplitBar = function(cfg){
34294     
34295     /** @private */
34296     
34297     //{
34298     //  dragElement : elm
34299     //  resizingElement: el,
34300         // optional..
34301     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34302     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34303         // existingProxy ???
34304     //}
34305     
34306     this.el = Roo.get(cfg.dragElement, true);
34307     this.el.dom.unselectable = "on";
34308     /** @private */
34309     this.resizingEl = Roo.get(cfg.resizingElement, true);
34310
34311     /**
34312      * @private
34313      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34314      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34315      * @type Number
34316      */
34317     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34318     
34319     /**
34320      * The minimum size of the resizing element. (Defaults to 0)
34321      * @type Number
34322      */
34323     this.minSize = 0;
34324     
34325     /**
34326      * The maximum size of the resizing element. (Defaults to 2000)
34327      * @type Number
34328      */
34329     this.maxSize = 2000;
34330     
34331     /**
34332      * Whether to animate the transition to the new size
34333      * @type Boolean
34334      */
34335     this.animate = false;
34336     
34337     /**
34338      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34339      * @type Boolean
34340      */
34341     this.useShim = false;
34342     
34343     /** @private */
34344     this.shim = null;
34345     
34346     if(!cfg.existingProxy){
34347         /** @private */
34348         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34349     }else{
34350         this.proxy = Roo.get(cfg.existingProxy).dom;
34351     }
34352     /** @private */
34353     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34354     
34355     /** @private */
34356     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34357     
34358     /** @private */
34359     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34360     
34361     /** @private */
34362     this.dragSpecs = {};
34363     
34364     /**
34365      * @private The adapter to use to positon and resize elements
34366      */
34367     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34368     this.adapter.init(this);
34369     
34370     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34371         /** @private */
34372         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34373         this.el.addClass("roo-splitbar-h");
34374     }else{
34375         /** @private */
34376         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34377         this.el.addClass("roo-splitbar-v");
34378     }
34379     
34380     this.addEvents({
34381         /**
34382          * @event resize
34383          * Fires when the splitter is moved (alias for {@link #event-moved})
34384          * @param {Roo.bootstrap.SplitBar} this
34385          * @param {Number} newSize the new width or height
34386          */
34387         "resize" : true,
34388         /**
34389          * @event moved
34390          * Fires when the splitter is moved
34391          * @param {Roo.bootstrap.SplitBar} this
34392          * @param {Number} newSize the new width or height
34393          */
34394         "moved" : true,
34395         /**
34396          * @event beforeresize
34397          * Fires before the splitter is dragged
34398          * @param {Roo.bootstrap.SplitBar} this
34399          */
34400         "beforeresize" : true,
34401
34402         "beforeapply" : true
34403     });
34404
34405     Roo.util.Observable.call(this);
34406 };
34407
34408 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34409     onStartProxyDrag : function(x, y){
34410         this.fireEvent("beforeresize", this);
34411         if(!this.overlay){
34412             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34413             o.unselectable();
34414             o.enableDisplayMode("block");
34415             // all splitbars share the same overlay
34416             Roo.bootstrap.SplitBar.prototype.overlay = o;
34417         }
34418         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34419         this.overlay.show();
34420         Roo.get(this.proxy).setDisplayed("block");
34421         var size = this.adapter.getElementSize(this);
34422         this.activeMinSize = this.getMinimumSize();;
34423         this.activeMaxSize = this.getMaximumSize();;
34424         var c1 = size - this.activeMinSize;
34425         var c2 = Math.max(this.activeMaxSize - size, 0);
34426         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34427             this.dd.resetConstraints();
34428             this.dd.setXConstraint(
34429                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34430                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34431             );
34432             this.dd.setYConstraint(0, 0);
34433         }else{
34434             this.dd.resetConstraints();
34435             this.dd.setXConstraint(0, 0);
34436             this.dd.setYConstraint(
34437                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34438                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34439             );
34440          }
34441         this.dragSpecs.startSize = size;
34442         this.dragSpecs.startPoint = [x, y];
34443         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34444     },
34445     
34446     /** 
34447      * @private Called after the drag operation by the DDProxy
34448      */
34449     onEndProxyDrag : function(e){
34450         Roo.get(this.proxy).setDisplayed(false);
34451         var endPoint = Roo.lib.Event.getXY(e);
34452         if(this.overlay){
34453             this.overlay.hide();
34454         }
34455         var newSize;
34456         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34457             newSize = this.dragSpecs.startSize + 
34458                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34459                     endPoint[0] - this.dragSpecs.startPoint[0] :
34460                     this.dragSpecs.startPoint[0] - endPoint[0]
34461                 );
34462         }else{
34463             newSize = this.dragSpecs.startSize + 
34464                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34465                     endPoint[1] - this.dragSpecs.startPoint[1] :
34466                     this.dragSpecs.startPoint[1] - endPoint[1]
34467                 );
34468         }
34469         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34470         if(newSize != this.dragSpecs.startSize){
34471             if(this.fireEvent('beforeapply', this, newSize) !== false){
34472                 this.adapter.setElementSize(this, newSize);
34473                 this.fireEvent("moved", this, newSize);
34474                 this.fireEvent("resize", this, newSize);
34475             }
34476         }
34477     },
34478     
34479     /**
34480      * Get the adapter this SplitBar uses
34481      * @return The adapter object
34482      */
34483     getAdapter : function(){
34484         return this.adapter;
34485     },
34486     
34487     /**
34488      * Set the adapter this SplitBar uses
34489      * @param {Object} adapter A SplitBar adapter object
34490      */
34491     setAdapter : function(adapter){
34492         this.adapter = adapter;
34493         this.adapter.init(this);
34494     },
34495     
34496     /**
34497      * Gets the minimum size for the resizing element
34498      * @return {Number} The minimum size
34499      */
34500     getMinimumSize : function(){
34501         return this.minSize;
34502     },
34503     
34504     /**
34505      * Sets the minimum size for the resizing element
34506      * @param {Number} minSize The minimum size
34507      */
34508     setMinimumSize : function(minSize){
34509         this.minSize = minSize;
34510     },
34511     
34512     /**
34513      * Gets the maximum size for the resizing element
34514      * @return {Number} The maximum size
34515      */
34516     getMaximumSize : function(){
34517         return this.maxSize;
34518     },
34519     
34520     /**
34521      * Sets the maximum size for the resizing element
34522      * @param {Number} maxSize The maximum size
34523      */
34524     setMaximumSize : function(maxSize){
34525         this.maxSize = maxSize;
34526     },
34527     
34528     /**
34529      * Sets the initialize size for the resizing element
34530      * @param {Number} size The initial size
34531      */
34532     setCurrentSize : function(size){
34533         var oldAnimate = this.animate;
34534         this.animate = false;
34535         this.adapter.setElementSize(this, size);
34536         this.animate = oldAnimate;
34537     },
34538     
34539     /**
34540      * Destroy this splitbar. 
34541      * @param {Boolean} removeEl True to remove the element
34542      */
34543     destroy : function(removeEl){
34544         if(this.shim){
34545             this.shim.remove();
34546         }
34547         this.dd.unreg();
34548         this.proxy.parentNode.removeChild(this.proxy);
34549         if(removeEl){
34550             this.el.remove();
34551         }
34552     }
34553 });
34554
34555 /**
34556  * @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.
34557  */
34558 Roo.bootstrap.SplitBar.createProxy = function(dir){
34559     var proxy = new Roo.Element(document.createElement("div"));
34560     proxy.unselectable();
34561     var cls = 'roo-splitbar-proxy';
34562     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34563     document.body.appendChild(proxy.dom);
34564     return proxy.dom;
34565 };
34566
34567 /** 
34568  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34569  * Default Adapter. It assumes the splitter and resizing element are not positioned
34570  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34571  */
34572 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34573 };
34574
34575 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34576     // do nothing for now
34577     init : function(s){
34578     
34579     },
34580     /**
34581      * Called before drag operations to get the current size of the resizing element. 
34582      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34583      */
34584      getElementSize : function(s){
34585         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34586             return s.resizingEl.getWidth();
34587         }else{
34588             return s.resizingEl.getHeight();
34589         }
34590     },
34591     
34592     /**
34593      * Called after drag operations to set the size of the resizing element.
34594      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34595      * @param {Number} newSize The new size to set
34596      * @param {Function} onComplete A function to be invoked when resizing is complete
34597      */
34598     setElementSize : function(s, newSize, onComplete){
34599         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34600             if(!s.animate){
34601                 s.resizingEl.setWidth(newSize);
34602                 if(onComplete){
34603                     onComplete(s, newSize);
34604                 }
34605             }else{
34606                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34607             }
34608         }else{
34609             
34610             if(!s.animate){
34611                 s.resizingEl.setHeight(newSize);
34612                 if(onComplete){
34613                     onComplete(s, newSize);
34614                 }
34615             }else{
34616                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34617             }
34618         }
34619     }
34620 };
34621
34622 /** 
34623  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34624  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34625  * Adapter that  moves the splitter element to align with the resized sizing element. 
34626  * Used with an absolute positioned SplitBar.
34627  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34628  * document.body, make sure you assign an id to the body element.
34629  */
34630 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34631     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34632     this.container = Roo.get(container);
34633 };
34634
34635 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34636     init : function(s){
34637         this.basic.init(s);
34638     },
34639     
34640     getElementSize : function(s){
34641         return this.basic.getElementSize(s);
34642     },
34643     
34644     setElementSize : function(s, newSize, onComplete){
34645         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34646     },
34647     
34648     moveSplitter : function(s){
34649         var yes = Roo.bootstrap.SplitBar;
34650         switch(s.placement){
34651             case yes.LEFT:
34652                 s.el.setX(s.resizingEl.getRight());
34653                 break;
34654             case yes.RIGHT:
34655                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34656                 break;
34657             case yes.TOP:
34658                 s.el.setY(s.resizingEl.getBottom());
34659                 break;
34660             case yes.BOTTOM:
34661                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34662                 break;
34663         }
34664     }
34665 };
34666
34667 /**
34668  * Orientation constant - Create a vertical SplitBar
34669  * @static
34670  * @type Number
34671  */
34672 Roo.bootstrap.SplitBar.VERTICAL = 1;
34673
34674 /**
34675  * Orientation constant - Create a horizontal SplitBar
34676  * @static
34677  * @type Number
34678  */
34679 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34680
34681 /**
34682  * Placement constant - The resizing element is to the left of the splitter element
34683  * @static
34684  * @type Number
34685  */
34686 Roo.bootstrap.SplitBar.LEFT = 1;
34687
34688 /**
34689  * Placement constant - The resizing element is to the right of the splitter element
34690  * @static
34691  * @type Number
34692  */
34693 Roo.bootstrap.SplitBar.RIGHT = 2;
34694
34695 /**
34696  * Placement constant - The resizing element is positioned above the splitter element
34697  * @static
34698  * @type Number
34699  */
34700 Roo.bootstrap.SplitBar.TOP = 3;
34701
34702 /**
34703  * Placement constant - The resizing element is positioned under splitter element
34704  * @static
34705  * @type Number
34706  */
34707 Roo.bootstrap.SplitBar.BOTTOM = 4;
34708 Roo.namespace("Roo.bootstrap.layout");/*
34709  * Based on:
34710  * Ext JS Library 1.1.1
34711  * Copyright(c) 2006-2007, Ext JS, LLC.
34712  *
34713  * Originally Released Under LGPL - original licence link has changed is not relivant.
34714  *
34715  * Fork - LGPL
34716  * <script type="text/javascript">
34717  */
34718
34719 /**
34720  * @class Roo.bootstrap.layout.Manager
34721  * @extends Roo.bootstrap.Component
34722  * Base class for layout managers.
34723  */
34724 Roo.bootstrap.layout.Manager = function(config)
34725 {
34726     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34727
34728
34729
34730
34731
34732     /** false to disable window resize monitoring @type Boolean */
34733     this.monitorWindowResize = true;
34734     this.regions = {};
34735     this.addEvents({
34736         /**
34737          * @event layout
34738          * Fires when a layout is performed.
34739          * @param {Roo.LayoutManager} this
34740          */
34741         "layout" : true,
34742         /**
34743          * @event regionresized
34744          * Fires when the user resizes a region.
34745          * @param {Roo.LayoutRegion} region The resized region
34746          * @param {Number} newSize The new size (width for east/west, height for north/south)
34747          */
34748         "regionresized" : true,
34749         /**
34750          * @event regioncollapsed
34751          * Fires when a region is collapsed.
34752          * @param {Roo.LayoutRegion} region The collapsed region
34753          */
34754         "regioncollapsed" : true,
34755         /**
34756          * @event regionexpanded
34757          * Fires when a region is expanded.
34758          * @param {Roo.LayoutRegion} region The expanded region
34759          */
34760         "regionexpanded" : true
34761     });
34762     this.updating = false;
34763
34764     if (config.el) {
34765         this.el = Roo.get(config.el);
34766         this.initEvents();
34767     }
34768
34769 };
34770
34771 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34772
34773
34774     regions : null,
34775
34776     monitorWindowResize : true,
34777
34778
34779     updating : false,
34780
34781
34782     onRender : function(ct, position)
34783     {
34784         if(!this.el){
34785             this.el = Roo.get(ct);
34786             this.initEvents();
34787         }
34788         //this.fireEvent('render',this);
34789     },
34790
34791
34792     initEvents: function()
34793     {
34794
34795
34796         // ie scrollbar fix
34797         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34798             document.body.scroll = "no";
34799         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34800             this.el.position('relative');
34801         }
34802         this.id = this.el.id;
34803         this.el.addClass("roo-layout-container");
34804         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34805         if(this.el.dom != document.body ) {
34806             this.el.on('resize', this.layout,this);
34807             this.el.on('show', this.layout,this);
34808         }
34809
34810     },
34811
34812     /**
34813      * Returns true if this layout is currently being updated
34814      * @return {Boolean}
34815      */
34816     isUpdating : function(){
34817         return this.updating;
34818     },
34819
34820     /**
34821      * Suspend the LayoutManager from doing auto-layouts while
34822      * making multiple add or remove calls
34823      */
34824     beginUpdate : function(){
34825         this.updating = true;
34826     },
34827
34828     /**
34829      * Restore auto-layouts and optionally disable the manager from performing a layout
34830      * @param {Boolean} noLayout true to disable a layout update
34831      */
34832     endUpdate : function(noLayout){
34833         this.updating = false;
34834         if(!noLayout){
34835             this.layout();
34836         }
34837     },
34838
34839     layout: function(){
34840         // abstract...
34841     },
34842
34843     onRegionResized : function(region, newSize){
34844         this.fireEvent("regionresized", region, newSize);
34845         this.layout();
34846     },
34847
34848     onRegionCollapsed : function(region){
34849         this.fireEvent("regioncollapsed", region);
34850     },
34851
34852     onRegionExpanded : function(region){
34853         this.fireEvent("regionexpanded", region);
34854     },
34855
34856     /**
34857      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34858      * performs box-model adjustments.
34859      * @return {Object} The size as an object {width: (the width), height: (the height)}
34860      */
34861     getViewSize : function()
34862     {
34863         var size;
34864         if(this.el.dom != document.body){
34865             size = this.el.getSize();
34866         }else{
34867             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34868         }
34869         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34870         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34871         return size;
34872     },
34873
34874     /**
34875      * Returns the Element this layout is bound to.
34876      * @return {Roo.Element}
34877      */
34878     getEl : function(){
34879         return this.el;
34880     },
34881
34882     /**
34883      * Returns the specified region.
34884      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34885      * @return {Roo.LayoutRegion}
34886      */
34887     getRegion : function(target){
34888         return this.regions[target.toLowerCase()];
34889     },
34890
34891     onWindowResize : function(){
34892         if(this.monitorWindowResize){
34893             this.layout();
34894         }
34895     }
34896 });
34897 /*
34898  * Based on:
34899  * Ext JS Library 1.1.1
34900  * Copyright(c) 2006-2007, Ext JS, LLC.
34901  *
34902  * Originally Released Under LGPL - original licence link has changed is not relivant.
34903  *
34904  * Fork - LGPL
34905  * <script type="text/javascript">
34906  */
34907 /**
34908  * @class Roo.bootstrap.layout.Border
34909  * @extends Roo.bootstrap.layout.Manager
34910  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34911  * please see: examples/bootstrap/nested.html<br><br>
34912  
34913 <b>The container the layout is rendered into can be either the body element or any other element.
34914 If it is not the body element, the container needs to either be an absolute positioned element,
34915 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34916 the container size if it is not the body element.</b>
34917
34918 * @constructor
34919 * Create a new Border
34920 * @param {Object} config Configuration options
34921  */
34922 Roo.bootstrap.layout.Border = function(config){
34923     config = config || {};
34924     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34925     
34926     
34927     
34928     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34929         if(config[region]){
34930             config[region].region = region;
34931             this.addRegion(config[region]);
34932         }
34933     },this);
34934     
34935 };
34936
34937 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34938
34939 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34940     /**
34941      * Creates and adds a new region if it doesn't already exist.
34942      * @param {String} target The target region key (north, south, east, west or center).
34943      * @param {Object} config The regions config object
34944      * @return {BorderLayoutRegion} The new region
34945      */
34946     addRegion : function(config)
34947     {
34948         if(!this.regions[config.region]){
34949             var r = this.factory(config);
34950             this.bindRegion(r);
34951         }
34952         return this.regions[config.region];
34953     },
34954
34955     // private (kinda)
34956     bindRegion : function(r){
34957         this.regions[r.config.region] = r;
34958         
34959         r.on("visibilitychange",    this.layout, this);
34960         r.on("paneladded",          this.layout, this);
34961         r.on("panelremoved",        this.layout, this);
34962         r.on("invalidated",         this.layout, this);
34963         r.on("resized",             this.onRegionResized, this);
34964         r.on("collapsed",           this.onRegionCollapsed, this);
34965         r.on("expanded",            this.onRegionExpanded, this);
34966     },
34967
34968     /**
34969      * Performs a layout update.
34970      */
34971     layout : function()
34972     {
34973         if(this.updating) {
34974             return;
34975         }
34976         
34977         // render all the rebions if they have not been done alreayd?
34978         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34979             if(this.regions[region] && !this.regions[region].bodyEl){
34980                 this.regions[region].onRender(this.el)
34981             }
34982         },this);
34983         
34984         var size = this.getViewSize();
34985         var w = size.width;
34986         var h = size.height;
34987         var centerW = w;
34988         var centerH = h;
34989         var centerY = 0;
34990         var centerX = 0;
34991         //var x = 0, y = 0;
34992
34993         var rs = this.regions;
34994         var north = rs["north"];
34995         var south = rs["south"]; 
34996         var west = rs["west"];
34997         var east = rs["east"];
34998         var center = rs["center"];
34999         //if(this.hideOnLayout){ // not supported anymore
35000             //c.el.setStyle("display", "none");
35001         //}
35002         if(north && north.isVisible()){
35003             var b = north.getBox();
35004             var m = north.getMargins();
35005             b.width = w - (m.left+m.right);
35006             b.x = m.left;
35007             b.y = m.top;
35008             centerY = b.height + b.y + m.bottom;
35009             centerH -= centerY;
35010             north.updateBox(this.safeBox(b));
35011         }
35012         if(south && south.isVisible()){
35013             var b = south.getBox();
35014             var m = south.getMargins();
35015             b.width = w - (m.left+m.right);
35016             b.x = m.left;
35017             var totalHeight = (b.height + m.top + m.bottom);
35018             b.y = h - totalHeight + m.top;
35019             centerH -= totalHeight;
35020             south.updateBox(this.safeBox(b));
35021         }
35022         if(west && west.isVisible()){
35023             var b = west.getBox();
35024             var m = west.getMargins();
35025             b.height = centerH - (m.top+m.bottom);
35026             b.x = m.left;
35027             b.y = centerY + m.top;
35028             var totalWidth = (b.width + m.left + m.right);
35029             centerX += totalWidth;
35030             centerW -= totalWidth;
35031             west.updateBox(this.safeBox(b));
35032         }
35033         if(east && east.isVisible()){
35034             var b = east.getBox();
35035             var m = east.getMargins();
35036             b.height = centerH - (m.top+m.bottom);
35037             var totalWidth = (b.width + m.left + m.right);
35038             b.x = w - totalWidth + m.left;
35039             b.y = centerY + m.top;
35040             centerW -= totalWidth;
35041             east.updateBox(this.safeBox(b));
35042         }
35043         if(center){
35044             var m = center.getMargins();
35045             var centerBox = {
35046                 x: centerX + m.left,
35047                 y: centerY + m.top,
35048                 width: centerW - (m.left+m.right),
35049                 height: centerH - (m.top+m.bottom)
35050             };
35051             //if(this.hideOnLayout){
35052                 //center.el.setStyle("display", "block");
35053             //}
35054             center.updateBox(this.safeBox(centerBox));
35055         }
35056         this.el.repaint();
35057         this.fireEvent("layout", this);
35058     },
35059
35060     // private
35061     safeBox : function(box){
35062         box.width = Math.max(0, box.width);
35063         box.height = Math.max(0, box.height);
35064         return box;
35065     },
35066
35067     /**
35068      * Adds a ContentPanel (or subclass) to this layout.
35069      * @param {String} target The target region key (north, south, east, west or center).
35070      * @param {Roo.ContentPanel} panel The panel to add
35071      * @return {Roo.ContentPanel} The added panel
35072      */
35073     add : function(target, panel){
35074          
35075         target = target.toLowerCase();
35076         return this.regions[target].add(panel);
35077     },
35078
35079     /**
35080      * Remove a ContentPanel (or subclass) to this layout.
35081      * @param {String} target The target region key (north, south, east, west or center).
35082      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35083      * @return {Roo.ContentPanel} The removed panel
35084      */
35085     remove : function(target, panel){
35086         target = target.toLowerCase();
35087         return this.regions[target].remove(panel);
35088     },
35089
35090     /**
35091      * Searches all regions for a panel with the specified id
35092      * @param {String} panelId
35093      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35094      */
35095     findPanel : function(panelId){
35096         var rs = this.regions;
35097         for(var target in rs){
35098             if(typeof rs[target] != "function"){
35099                 var p = rs[target].getPanel(panelId);
35100                 if(p){
35101                     return p;
35102                 }
35103             }
35104         }
35105         return null;
35106     },
35107
35108     /**
35109      * Searches all regions for a panel with the specified id and activates (shows) it.
35110      * @param {String/ContentPanel} panelId The panels id or the panel itself
35111      * @return {Roo.ContentPanel} The shown panel or null
35112      */
35113     showPanel : function(panelId) {
35114       var rs = this.regions;
35115       for(var target in rs){
35116          var r = rs[target];
35117          if(typeof r != "function"){
35118             if(r.hasPanel(panelId)){
35119                return r.showPanel(panelId);
35120             }
35121          }
35122       }
35123       return null;
35124    },
35125
35126    /**
35127      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35128      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35129      */
35130    /*
35131     restoreState : function(provider){
35132         if(!provider){
35133             provider = Roo.state.Manager;
35134         }
35135         var sm = new Roo.LayoutStateManager();
35136         sm.init(this, provider);
35137     },
35138 */
35139  
35140  
35141     /**
35142      * Adds a xtype elements to the layout.
35143      * <pre><code>
35144
35145 layout.addxtype({
35146        xtype : 'ContentPanel',
35147        region: 'west',
35148        items: [ .... ]
35149    }
35150 );
35151
35152 layout.addxtype({
35153         xtype : 'NestedLayoutPanel',
35154         region: 'west',
35155         layout: {
35156            center: { },
35157            west: { }   
35158         },
35159         items : [ ... list of content panels or nested layout panels.. ]
35160    }
35161 );
35162 </code></pre>
35163      * @param {Object} cfg Xtype definition of item to add.
35164      */
35165     addxtype : function(cfg)
35166     {
35167         // basically accepts a pannel...
35168         // can accept a layout region..!?!?
35169         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35170         
35171         
35172         // theory?  children can only be panels??
35173         
35174         //if (!cfg.xtype.match(/Panel$/)) {
35175         //    return false;
35176         //}
35177         var ret = false;
35178         
35179         if (typeof(cfg.region) == 'undefined') {
35180             Roo.log("Failed to add Panel, region was not set");
35181             Roo.log(cfg);
35182             return false;
35183         }
35184         var region = cfg.region;
35185         delete cfg.region;
35186         
35187           
35188         var xitems = [];
35189         if (cfg.items) {
35190             xitems = cfg.items;
35191             delete cfg.items;
35192         }
35193         var nb = false;
35194         
35195         switch(cfg.xtype) 
35196         {
35197             case 'Content':  // ContentPanel (el, cfg)
35198             case 'Scroll':  // ContentPanel (el, cfg)
35199             case 'View': 
35200                 cfg.autoCreate = true;
35201                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35202                 //} else {
35203                 //    var el = this.el.createChild();
35204                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35205                 //}
35206                 
35207                 this.add(region, ret);
35208                 break;
35209             
35210             /*
35211             case 'TreePanel': // our new panel!
35212                 cfg.el = this.el.createChild();
35213                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35214                 this.add(region, ret);
35215                 break;
35216             */
35217             
35218             case 'Nest': 
35219                 // create a new Layout (which is  a Border Layout...
35220                 
35221                 var clayout = cfg.layout;
35222                 clayout.el  = this.el.createChild();
35223                 clayout.items   = clayout.items  || [];
35224                 
35225                 delete cfg.layout;
35226                 
35227                 // replace this exitems with the clayout ones..
35228                 xitems = clayout.items;
35229                  
35230                 // force background off if it's in center...
35231                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35232                     cfg.background = false;
35233                 }
35234                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35235                 
35236                 
35237                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35238                 //console.log('adding nested layout panel '  + cfg.toSource());
35239                 this.add(region, ret);
35240                 nb = {}; /// find first...
35241                 break;
35242             
35243             case 'Grid':
35244                 
35245                 // needs grid and region
35246                 
35247                 //var el = this.getRegion(region).el.createChild();
35248                 /*
35249                  *var el = this.el.createChild();
35250                 // create the grid first...
35251                 cfg.grid.container = el;
35252                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35253                 */
35254                 
35255                 if (region == 'center' && this.active ) {
35256                     cfg.background = false;
35257                 }
35258                 
35259                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35260                 
35261                 this.add(region, ret);
35262                 /*
35263                 if (cfg.background) {
35264                     // render grid on panel activation (if panel background)
35265                     ret.on('activate', function(gp) {
35266                         if (!gp.grid.rendered) {
35267                     //        gp.grid.render(el);
35268                         }
35269                     });
35270                 } else {
35271                   //  cfg.grid.render(el);
35272                 }
35273                 */
35274                 break;
35275            
35276            
35277             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35278                 // it was the old xcomponent building that caused this before.
35279                 // espeically if border is the top element in the tree.
35280                 ret = this;
35281                 break; 
35282                 
35283                     
35284                 
35285                 
35286                 
35287             default:
35288                 /*
35289                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35290                     
35291                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35292                     this.add(region, ret);
35293                 } else {
35294                 */
35295                     Roo.log(cfg);
35296                     throw "Can not add '" + cfg.xtype + "' to Border";
35297                     return null;
35298              
35299                                 
35300              
35301         }
35302         this.beginUpdate();
35303         // add children..
35304         var region = '';
35305         var abn = {};
35306         Roo.each(xitems, function(i)  {
35307             region = nb && i.region ? i.region : false;
35308             
35309             var add = ret.addxtype(i);
35310            
35311             if (region) {
35312                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35313                 if (!i.background) {
35314                     abn[region] = nb[region] ;
35315                 }
35316             }
35317             
35318         });
35319         this.endUpdate();
35320
35321         // make the last non-background panel active..
35322         //if (nb) { Roo.log(abn); }
35323         if (nb) {
35324             
35325             for(var r in abn) {
35326                 region = this.getRegion(r);
35327                 if (region) {
35328                     // tried using nb[r], but it does not work..
35329                      
35330                     region.showPanel(abn[r]);
35331                    
35332                 }
35333             }
35334         }
35335         return ret;
35336         
35337     },
35338     
35339     
35340 // private
35341     factory : function(cfg)
35342     {
35343         
35344         var validRegions = Roo.bootstrap.layout.Border.regions;
35345
35346         var target = cfg.region;
35347         cfg.mgr = this;
35348         
35349         var r = Roo.bootstrap.layout;
35350         Roo.log(target);
35351         switch(target){
35352             case "north":
35353                 return new r.North(cfg);
35354             case "south":
35355                 return new r.South(cfg);
35356             case "east":
35357                 return new r.East(cfg);
35358             case "west":
35359                 return new r.West(cfg);
35360             case "center":
35361                 return new r.Center(cfg);
35362         }
35363         throw 'Layout region "'+target+'" not supported.';
35364     }
35365     
35366     
35367 });
35368  /*
35369  * Based on:
35370  * Ext JS Library 1.1.1
35371  * Copyright(c) 2006-2007, Ext JS, LLC.
35372  *
35373  * Originally Released Under LGPL - original licence link has changed is not relivant.
35374  *
35375  * Fork - LGPL
35376  * <script type="text/javascript">
35377  */
35378  
35379 /**
35380  * @class Roo.bootstrap.layout.Basic
35381  * @extends Roo.util.Observable
35382  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35383  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35384  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35385  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35386  * @cfg {string}   region  the region that it inhabits..
35387  * @cfg {bool}   skipConfig skip config?
35388  * 
35389
35390  */
35391 Roo.bootstrap.layout.Basic = function(config){
35392     
35393     this.mgr = config.mgr;
35394     
35395     this.position = config.region;
35396     
35397     var skipConfig = config.skipConfig;
35398     
35399     this.events = {
35400         /**
35401          * @scope Roo.BasicLayoutRegion
35402          */
35403         
35404         /**
35405          * @event beforeremove
35406          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35407          * @param {Roo.LayoutRegion} this
35408          * @param {Roo.ContentPanel} panel The panel
35409          * @param {Object} e The cancel event object
35410          */
35411         "beforeremove" : true,
35412         /**
35413          * @event invalidated
35414          * Fires when the layout for this region is changed.
35415          * @param {Roo.LayoutRegion} this
35416          */
35417         "invalidated" : true,
35418         /**
35419          * @event visibilitychange
35420          * Fires when this region is shown or hidden 
35421          * @param {Roo.LayoutRegion} this
35422          * @param {Boolean} visibility true or false
35423          */
35424         "visibilitychange" : true,
35425         /**
35426          * @event paneladded
35427          * Fires when a panel is added. 
35428          * @param {Roo.LayoutRegion} this
35429          * @param {Roo.ContentPanel} panel The panel
35430          */
35431         "paneladded" : true,
35432         /**
35433          * @event panelremoved
35434          * Fires when a panel is removed. 
35435          * @param {Roo.LayoutRegion} this
35436          * @param {Roo.ContentPanel} panel The panel
35437          */
35438         "panelremoved" : true,
35439         /**
35440          * @event beforecollapse
35441          * Fires when this region before collapse.
35442          * @param {Roo.LayoutRegion} this
35443          */
35444         "beforecollapse" : true,
35445         /**
35446          * @event collapsed
35447          * Fires when this region is collapsed.
35448          * @param {Roo.LayoutRegion} this
35449          */
35450         "collapsed" : true,
35451         /**
35452          * @event expanded
35453          * Fires when this region is expanded.
35454          * @param {Roo.LayoutRegion} this
35455          */
35456         "expanded" : true,
35457         /**
35458          * @event slideshow
35459          * Fires when this region is slid into view.
35460          * @param {Roo.LayoutRegion} this
35461          */
35462         "slideshow" : true,
35463         /**
35464          * @event slidehide
35465          * Fires when this region slides out of view. 
35466          * @param {Roo.LayoutRegion} this
35467          */
35468         "slidehide" : true,
35469         /**
35470          * @event panelactivated
35471          * Fires when a panel is activated. 
35472          * @param {Roo.LayoutRegion} this
35473          * @param {Roo.ContentPanel} panel The activated panel
35474          */
35475         "panelactivated" : true,
35476         /**
35477          * @event resized
35478          * Fires when the user resizes this region. 
35479          * @param {Roo.LayoutRegion} this
35480          * @param {Number} newSize The new size (width for east/west, height for north/south)
35481          */
35482         "resized" : true
35483     };
35484     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35485     this.panels = new Roo.util.MixedCollection();
35486     this.panels.getKey = this.getPanelId.createDelegate(this);
35487     this.box = null;
35488     this.activePanel = null;
35489     // ensure listeners are added...
35490     
35491     if (config.listeners || config.events) {
35492         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35493             listeners : config.listeners || {},
35494             events : config.events || {}
35495         });
35496     }
35497     
35498     if(skipConfig !== true){
35499         this.applyConfig(config);
35500     }
35501 };
35502
35503 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35504 {
35505     getPanelId : function(p){
35506         return p.getId();
35507     },
35508     
35509     applyConfig : function(config){
35510         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35511         this.config = config;
35512         
35513     },
35514     
35515     /**
35516      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35517      * the width, for horizontal (north, south) the height.
35518      * @param {Number} newSize The new width or height
35519      */
35520     resizeTo : function(newSize){
35521         var el = this.el ? this.el :
35522                  (this.activePanel ? this.activePanel.getEl() : null);
35523         if(el){
35524             switch(this.position){
35525                 case "east":
35526                 case "west":
35527                     el.setWidth(newSize);
35528                     this.fireEvent("resized", this, newSize);
35529                 break;
35530                 case "north":
35531                 case "south":
35532                     el.setHeight(newSize);
35533                     this.fireEvent("resized", this, newSize);
35534                 break;                
35535             }
35536         }
35537     },
35538     
35539     getBox : function(){
35540         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35541     },
35542     
35543     getMargins : function(){
35544         return this.margins;
35545     },
35546     
35547     updateBox : function(box){
35548         this.box = box;
35549         var el = this.activePanel.getEl();
35550         el.dom.style.left = box.x + "px";
35551         el.dom.style.top = box.y + "px";
35552         this.activePanel.setSize(box.width, box.height);
35553     },
35554     
35555     /**
35556      * Returns the container element for this region.
35557      * @return {Roo.Element}
35558      */
35559     getEl : function(){
35560         return this.activePanel;
35561     },
35562     
35563     /**
35564      * Returns true if this region is currently visible.
35565      * @return {Boolean}
35566      */
35567     isVisible : function(){
35568         return this.activePanel ? true : false;
35569     },
35570     
35571     setActivePanel : function(panel){
35572         panel = this.getPanel(panel);
35573         if(this.activePanel && this.activePanel != panel){
35574             this.activePanel.setActiveState(false);
35575             this.activePanel.getEl().setLeftTop(-10000,-10000);
35576         }
35577         this.activePanel = panel;
35578         panel.setActiveState(true);
35579         if(this.box){
35580             panel.setSize(this.box.width, this.box.height);
35581         }
35582         this.fireEvent("panelactivated", this, panel);
35583         this.fireEvent("invalidated");
35584     },
35585     
35586     /**
35587      * Show the specified panel.
35588      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35589      * @return {Roo.ContentPanel} The shown panel or null
35590      */
35591     showPanel : function(panel){
35592         panel = this.getPanel(panel);
35593         if(panel){
35594             this.setActivePanel(panel);
35595         }
35596         return panel;
35597     },
35598     
35599     /**
35600      * Get the active panel for this region.
35601      * @return {Roo.ContentPanel} The active panel or null
35602      */
35603     getActivePanel : function(){
35604         return this.activePanel;
35605     },
35606     
35607     /**
35608      * Add the passed ContentPanel(s)
35609      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35610      * @return {Roo.ContentPanel} The panel added (if only one was added)
35611      */
35612     add : function(panel){
35613         if(arguments.length > 1){
35614             for(var i = 0, len = arguments.length; i < len; i++) {
35615                 this.add(arguments[i]);
35616             }
35617             return null;
35618         }
35619         if(this.hasPanel(panel)){
35620             this.showPanel(panel);
35621             return panel;
35622         }
35623         var el = panel.getEl();
35624         if(el.dom.parentNode != this.mgr.el.dom){
35625             this.mgr.el.dom.appendChild(el.dom);
35626         }
35627         if(panel.setRegion){
35628             panel.setRegion(this);
35629         }
35630         this.panels.add(panel);
35631         el.setStyle("position", "absolute");
35632         if(!panel.background){
35633             this.setActivePanel(panel);
35634             if(this.config.initialSize && this.panels.getCount()==1){
35635                 this.resizeTo(this.config.initialSize);
35636             }
35637         }
35638         this.fireEvent("paneladded", this, panel);
35639         return panel;
35640     },
35641     
35642     /**
35643      * Returns true if the panel is in this region.
35644      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35645      * @return {Boolean}
35646      */
35647     hasPanel : function(panel){
35648         if(typeof panel == "object"){ // must be panel obj
35649             panel = panel.getId();
35650         }
35651         return this.getPanel(panel) ? true : false;
35652     },
35653     
35654     /**
35655      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35656      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35657      * @param {Boolean} preservePanel Overrides the config preservePanel option
35658      * @return {Roo.ContentPanel} The panel that was removed
35659      */
35660     remove : function(panel, preservePanel){
35661         panel = this.getPanel(panel);
35662         if(!panel){
35663             return null;
35664         }
35665         var e = {};
35666         this.fireEvent("beforeremove", this, panel, e);
35667         if(e.cancel === true){
35668             return null;
35669         }
35670         var panelId = panel.getId();
35671         this.panels.removeKey(panelId);
35672         return panel;
35673     },
35674     
35675     /**
35676      * Returns the panel specified or null if it's not in this region.
35677      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35678      * @return {Roo.ContentPanel}
35679      */
35680     getPanel : function(id){
35681         if(typeof id == "object"){ // must be panel obj
35682             return id;
35683         }
35684         return this.panels.get(id);
35685     },
35686     
35687     /**
35688      * Returns this regions position (north/south/east/west/center).
35689      * @return {String} 
35690      */
35691     getPosition: function(){
35692         return this.position;    
35693     }
35694 });/*
35695  * Based on:
35696  * Ext JS Library 1.1.1
35697  * Copyright(c) 2006-2007, Ext JS, LLC.
35698  *
35699  * Originally Released Under LGPL - original licence link has changed is not relivant.
35700  *
35701  * Fork - LGPL
35702  * <script type="text/javascript">
35703  */
35704  
35705 /**
35706  * @class Roo.bootstrap.layout.Region
35707  * @extends Roo.bootstrap.layout.Basic
35708  * This class represents a region in a layout manager.
35709  
35710  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35711  * @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})
35712  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35713  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35714  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35715  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35716  * @cfg {String}    title           The title for the region (overrides panel titles)
35717  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35718  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35719  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35720  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35721  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35722  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35723  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35724  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35725  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35726  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35727
35728  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35729  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35730  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35731  * @cfg {Number}    width           For East/West panels
35732  * @cfg {Number}    height          For North/South panels
35733  * @cfg {Boolean}   split           To show the splitter
35734  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35735  * 
35736  * @cfg {string}   cls             Extra CSS classes to add to region
35737  * 
35738  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35739  * @cfg {string}   region  the region that it inhabits..
35740  *
35741
35742  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35743  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35744
35745  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35746  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35747  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35748  */
35749 Roo.bootstrap.layout.Region = function(config)
35750 {
35751     this.applyConfig(config);
35752
35753     var mgr = config.mgr;
35754     var pos = config.region;
35755     config.skipConfig = true;
35756     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35757     
35758     if (mgr.el) {
35759         this.onRender(mgr.el);   
35760     }
35761      
35762     this.visible = true;
35763     this.collapsed = false;
35764     this.unrendered_panels = [];
35765 };
35766
35767 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35768
35769     position: '', // set by wrapper (eg. north/south etc..)
35770     unrendered_panels : null,  // unrendered panels.
35771     createBody : function(){
35772         /** This region's body element 
35773         * @type Roo.Element */
35774         this.bodyEl = this.el.createChild({
35775                 tag: "div",
35776                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35777         });
35778     },
35779
35780     onRender: function(ctr, pos)
35781     {
35782         var dh = Roo.DomHelper;
35783         /** This region's container element 
35784         * @type Roo.Element */
35785         this.el = dh.append(ctr.dom, {
35786                 tag: "div",
35787                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35788             }, true);
35789         /** This region's title element 
35790         * @type Roo.Element */
35791     
35792         this.titleEl = dh.append(this.el.dom,
35793             {
35794                     tag: "div",
35795                     unselectable: "on",
35796                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35797                     children:[
35798                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35799                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35800                     ]}, true);
35801         
35802         this.titleEl.enableDisplayMode();
35803         /** This region's title text element 
35804         * @type HTMLElement */
35805         this.titleTextEl = this.titleEl.dom.firstChild;
35806         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35807         /*
35808         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35809         this.closeBtn.enableDisplayMode();
35810         this.closeBtn.on("click", this.closeClicked, this);
35811         this.closeBtn.hide();
35812     */
35813         this.createBody(this.config);
35814         if(this.config.hideWhenEmpty){
35815             this.hide();
35816             this.on("paneladded", this.validateVisibility, this);
35817             this.on("panelremoved", this.validateVisibility, this);
35818         }
35819         if(this.autoScroll){
35820             this.bodyEl.setStyle("overflow", "auto");
35821         }else{
35822             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35823         }
35824         //if(c.titlebar !== false){
35825             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35826                 this.titleEl.hide();
35827             }else{
35828                 this.titleEl.show();
35829                 if(this.config.title){
35830                     this.titleTextEl.innerHTML = this.config.title;
35831                 }
35832             }
35833         //}
35834         if(this.config.collapsed){
35835             this.collapse(true);
35836         }
35837         if(this.config.hidden){
35838             this.hide();
35839         }
35840         
35841         if (this.unrendered_panels && this.unrendered_panels.length) {
35842             for (var i =0;i< this.unrendered_panels.length; i++) {
35843                 this.add(this.unrendered_panels[i]);
35844             }
35845             this.unrendered_panels = null;
35846             
35847         }
35848         
35849     },
35850     
35851     applyConfig : function(c)
35852     {
35853         /*
35854          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35855             var dh = Roo.DomHelper;
35856             if(c.titlebar !== false){
35857                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35858                 this.collapseBtn.on("click", this.collapse, this);
35859                 this.collapseBtn.enableDisplayMode();
35860                 /*
35861                 if(c.showPin === true || this.showPin){
35862                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35863                     this.stickBtn.enableDisplayMode();
35864                     this.stickBtn.on("click", this.expand, this);
35865                     this.stickBtn.hide();
35866                 }
35867                 
35868             }
35869             */
35870             /** This region's collapsed element
35871             * @type Roo.Element */
35872             /*
35873              *
35874             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35875                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35876             ]}, true);
35877             
35878             if(c.floatable !== false){
35879                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35880                this.collapsedEl.on("click", this.collapseClick, this);
35881             }
35882
35883             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35884                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35885                    id: "message", unselectable: "on", style:{"float":"left"}});
35886                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35887              }
35888             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35889             this.expandBtn.on("click", this.expand, this);
35890             
35891         }
35892         
35893         if(this.collapseBtn){
35894             this.collapseBtn.setVisible(c.collapsible == true);
35895         }
35896         
35897         this.cmargins = c.cmargins || this.cmargins ||
35898                          (this.position == "west" || this.position == "east" ?
35899                              {top: 0, left: 2, right:2, bottom: 0} :
35900                              {top: 2, left: 0, right:0, bottom: 2});
35901         */
35902         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35903         
35904         
35905         this.bottomTabs = c.tabPosition != "top";
35906         
35907         this.autoScroll = c.autoScroll || false;
35908         
35909         
35910        
35911         
35912         this.duration = c.duration || .30;
35913         this.slideDuration = c.slideDuration || .45;
35914         this.config = c;
35915        
35916     },
35917     /**
35918      * Returns true if this region is currently visible.
35919      * @return {Boolean}
35920      */
35921     isVisible : function(){
35922         return this.visible;
35923     },
35924
35925     /**
35926      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35927      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35928      */
35929     //setCollapsedTitle : function(title){
35930     //    title = title || "&#160;";
35931      //   if(this.collapsedTitleTextEl){
35932       //      this.collapsedTitleTextEl.innerHTML = title;
35933        // }
35934     //},
35935
35936     getBox : function(){
35937         var b;
35938       //  if(!this.collapsed){
35939             b = this.el.getBox(false, true);
35940        // }else{
35941           //  b = this.collapsedEl.getBox(false, true);
35942         //}
35943         return b;
35944     },
35945
35946     getMargins : function(){
35947         return this.margins;
35948         //return this.collapsed ? this.cmargins : this.margins;
35949     },
35950 /*
35951     highlight : function(){
35952         this.el.addClass("x-layout-panel-dragover");
35953     },
35954
35955     unhighlight : function(){
35956         this.el.removeClass("x-layout-panel-dragover");
35957     },
35958 */
35959     updateBox : function(box)
35960     {
35961         if (!this.bodyEl) {
35962             return; // not rendered yet..
35963         }
35964         
35965         this.box = box;
35966         if(!this.collapsed){
35967             this.el.dom.style.left = box.x + "px";
35968             this.el.dom.style.top = box.y + "px";
35969             this.updateBody(box.width, box.height);
35970         }else{
35971             this.collapsedEl.dom.style.left = box.x + "px";
35972             this.collapsedEl.dom.style.top = box.y + "px";
35973             this.collapsedEl.setSize(box.width, box.height);
35974         }
35975         if(this.tabs){
35976             this.tabs.autoSizeTabs();
35977         }
35978     },
35979
35980     updateBody : function(w, h)
35981     {
35982         if(w !== null){
35983             this.el.setWidth(w);
35984             w -= this.el.getBorderWidth("rl");
35985             if(this.config.adjustments){
35986                 w += this.config.adjustments[0];
35987             }
35988         }
35989         if(h !== null && h > 0){
35990             this.el.setHeight(h);
35991             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35992             h -= this.el.getBorderWidth("tb");
35993             if(this.config.adjustments){
35994                 h += this.config.adjustments[1];
35995             }
35996             this.bodyEl.setHeight(h);
35997             if(this.tabs){
35998                 h = this.tabs.syncHeight(h);
35999             }
36000         }
36001         if(this.panelSize){
36002             w = w !== null ? w : this.panelSize.width;
36003             h = h !== null ? h : this.panelSize.height;
36004         }
36005         if(this.activePanel){
36006             var el = this.activePanel.getEl();
36007             w = w !== null ? w : el.getWidth();
36008             h = h !== null ? h : el.getHeight();
36009             this.panelSize = {width: w, height: h};
36010             this.activePanel.setSize(w, h);
36011         }
36012         if(Roo.isIE && this.tabs){
36013             this.tabs.el.repaint();
36014         }
36015     },
36016
36017     /**
36018      * Returns the container element for this region.
36019      * @return {Roo.Element}
36020      */
36021     getEl : function(){
36022         return this.el;
36023     },
36024
36025     /**
36026      * Hides this region.
36027      */
36028     hide : function(){
36029         //if(!this.collapsed){
36030             this.el.dom.style.left = "-2000px";
36031             this.el.hide();
36032         //}else{
36033          //   this.collapsedEl.dom.style.left = "-2000px";
36034          //   this.collapsedEl.hide();
36035        // }
36036         this.visible = false;
36037         this.fireEvent("visibilitychange", this, false);
36038     },
36039
36040     /**
36041      * Shows this region if it was previously hidden.
36042      */
36043     show : function(){
36044         //if(!this.collapsed){
36045             this.el.show();
36046         //}else{
36047         //    this.collapsedEl.show();
36048        // }
36049         this.visible = true;
36050         this.fireEvent("visibilitychange", this, true);
36051     },
36052 /*
36053     closeClicked : function(){
36054         if(this.activePanel){
36055             this.remove(this.activePanel);
36056         }
36057     },
36058
36059     collapseClick : function(e){
36060         if(this.isSlid){
36061            e.stopPropagation();
36062            this.slideIn();
36063         }else{
36064            e.stopPropagation();
36065            this.slideOut();
36066         }
36067     },
36068 */
36069     /**
36070      * Collapses this region.
36071      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36072      */
36073     /*
36074     collapse : function(skipAnim, skipCheck = false){
36075         if(this.collapsed) {
36076             return;
36077         }
36078         
36079         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36080             
36081             this.collapsed = true;
36082             if(this.split){
36083                 this.split.el.hide();
36084             }
36085             if(this.config.animate && skipAnim !== true){
36086                 this.fireEvent("invalidated", this);
36087                 this.animateCollapse();
36088             }else{
36089                 this.el.setLocation(-20000,-20000);
36090                 this.el.hide();
36091                 this.collapsedEl.show();
36092                 this.fireEvent("collapsed", this);
36093                 this.fireEvent("invalidated", this);
36094             }
36095         }
36096         
36097     },
36098 */
36099     animateCollapse : function(){
36100         // overridden
36101     },
36102
36103     /**
36104      * Expands this region if it was previously collapsed.
36105      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36106      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36107      */
36108     /*
36109     expand : function(e, skipAnim){
36110         if(e) {
36111             e.stopPropagation();
36112         }
36113         if(!this.collapsed || this.el.hasActiveFx()) {
36114             return;
36115         }
36116         if(this.isSlid){
36117             this.afterSlideIn();
36118             skipAnim = true;
36119         }
36120         this.collapsed = false;
36121         if(this.config.animate && skipAnim !== true){
36122             this.animateExpand();
36123         }else{
36124             this.el.show();
36125             if(this.split){
36126                 this.split.el.show();
36127             }
36128             this.collapsedEl.setLocation(-2000,-2000);
36129             this.collapsedEl.hide();
36130             this.fireEvent("invalidated", this);
36131             this.fireEvent("expanded", this);
36132         }
36133     },
36134 */
36135     animateExpand : function(){
36136         // overridden
36137     },
36138
36139     initTabs : function()
36140     {
36141         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36142         
36143         var ts = new Roo.bootstrap.panel.Tabs({
36144                 el: this.bodyEl.dom,
36145                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36146                 disableTooltips: this.config.disableTabTips,
36147                 toolbar : this.config.toolbar
36148             });
36149         
36150         if(this.config.hideTabs){
36151             ts.stripWrap.setDisplayed(false);
36152         }
36153         this.tabs = ts;
36154         ts.resizeTabs = this.config.resizeTabs === true;
36155         ts.minTabWidth = this.config.minTabWidth || 40;
36156         ts.maxTabWidth = this.config.maxTabWidth || 250;
36157         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36158         ts.monitorResize = false;
36159         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36160         ts.bodyEl.addClass('roo-layout-tabs-body');
36161         this.panels.each(this.initPanelAsTab, this);
36162     },
36163
36164     initPanelAsTab : function(panel){
36165         var ti = this.tabs.addTab(
36166             panel.getEl().id,
36167             panel.getTitle(),
36168             null,
36169             this.config.closeOnTab && panel.isClosable(),
36170             panel.tpl
36171         );
36172         if(panel.tabTip !== undefined){
36173             ti.setTooltip(panel.tabTip);
36174         }
36175         ti.on("activate", function(){
36176               this.setActivePanel(panel);
36177         }, this);
36178         
36179         if(this.config.closeOnTab){
36180             ti.on("beforeclose", function(t, e){
36181                 e.cancel = true;
36182                 this.remove(panel);
36183             }, this);
36184         }
36185         
36186         panel.tabItem = ti;
36187         
36188         return ti;
36189     },
36190
36191     updatePanelTitle : function(panel, title)
36192     {
36193         if(this.activePanel == panel){
36194             this.updateTitle(title);
36195         }
36196         if(this.tabs){
36197             var ti = this.tabs.getTab(panel.getEl().id);
36198             ti.setText(title);
36199             if(panel.tabTip !== undefined){
36200                 ti.setTooltip(panel.tabTip);
36201             }
36202         }
36203     },
36204
36205     updateTitle : function(title){
36206         if(this.titleTextEl && !this.config.title){
36207             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36208         }
36209     },
36210
36211     setActivePanel : function(panel)
36212     {
36213         panel = this.getPanel(panel);
36214         if(this.activePanel && this.activePanel != panel){
36215             if(this.activePanel.setActiveState(false) === false){
36216                 return;
36217             }
36218         }
36219         this.activePanel = panel;
36220         panel.setActiveState(true);
36221         if(this.panelSize){
36222             panel.setSize(this.panelSize.width, this.panelSize.height);
36223         }
36224         if(this.closeBtn){
36225             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36226         }
36227         this.updateTitle(panel.getTitle());
36228         if(this.tabs){
36229             this.fireEvent("invalidated", this);
36230         }
36231         this.fireEvent("panelactivated", this, panel);
36232     },
36233
36234     /**
36235      * Shows the specified panel.
36236      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36237      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36238      */
36239     showPanel : function(panel)
36240     {
36241         panel = this.getPanel(panel);
36242         if(panel){
36243             if(this.tabs){
36244                 var tab = this.tabs.getTab(panel.getEl().id);
36245                 if(tab.isHidden()){
36246                     this.tabs.unhideTab(tab.id);
36247                 }
36248                 tab.activate();
36249             }else{
36250                 this.setActivePanel(panel);
36251             }
36252         }
36253         return panel;
36254     },
36255
36256     /**
36257      * Get the active panel for this region.
36258      * @return {Roo.ContentPanel} The active panel or null
36259      */
36260     getActivePanel : function(){
36261         return this.activePanel;
36262     },
36263
36264     validateVisibility : function(){
36265         if(this.panels.getCount() < 1){
36266             this.updateTitle("&#160;");
36267             this.closeBtn.hide();
36268             this.hide();
36269         }else{
36270             if(!this.isVisible()){
36271                 this.show();
36272             }
36273         }
36274     },
36275
36276     /**
36277      * Adds the passed ContentPanel(s) to this region.
36278      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36279      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36280      */
36281     add : function(panel)
36282     {
36283         if(arguments.length > 1){
36284             for(var i = 0, len = arguments.length; i < len; i++) {
36285                 this.add(arguments[i]);
36286             }
36287             return null;
36288         }
36289         
36290         // if we have not been rendered yet, then we can not really do much of this..
36291         if (!this.bodyEl) {
36292             this.unrendered_panels.push(panel);
36293             return panel;
36294         }
36295         
36296         
36297         
36298         
36299         if(this.hasPanel(panel)){
36300             this.showPanel(panel);
36301             return panel;
36302         }
36303         panel.setRegion(this);
36304         this.panels.add(panel);
36305        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36306             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36307             // and hide them... ???
36308             this.bodyEl.dom.appendChild(panel.getEl().dom);
36309             if(panel.background !== true){
36310                 this.setActivePanel(panel);
36311             }
36312             this.fireEvent("paneladded", this, panel);
36313             return panel;
36314         }
36315         */
36316         if(!this.tabs){
36317             this.initTabs();
36318         }else{
36319             this.initPanelAsTab(panel);
36320         }
36321         
36322         
36323         if(panel.background !== true){
36324             this.tabs.activate(panel.getEl().id);
36325         }
36326         this.fireEvent("paneladded", this, panel);
36327         return panel;
36328     },
36329
36330     /**
36331      * Hides the tab for the specified panel.
36332      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36333      */
36334     hidePanel : function(panel){
36335         if(this.tabs && (panel = this.getPanel(panel))){
36336             this.tabs.hideTab(panel.getEl().id);
36337         }
36338     },
36339
36340     /**
36341      * Unhides the tab for a previously hidden panel.
36342      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36343      */
36344     unhidePanel : function(panel){
36345         if(this.tabs && (panel = this.getPanel(panel))){
36346             this.tabs.unhideTab(panel.getEl().id);
36347         }
36348     },
36349
36350     clearPanels : function(){
36351         while(this.panels.getCount() > 0){
36352              this.remove(this.panels.first());
36353         }
36354     },
36355
36356     /**
36357      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36358      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36359      * @param {Boolean} preservePanel Overrides the config preservePanel option
36360      * @return {Roo.ContentPanel} The panel that was removed
36361      */
36362     remove : function(panel, preservePanel)
36363     {
36364         panel = this.getPanel(panel);
36365         if(!panel){
36366             return null;
36367         }
36368         var e = {};
36369         this.fireEvent("beforeremove", this, panel, e);
36370         if(e.cancel === true){
36371             return null;
36372         }
36373         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36374         var panelId = panel.getId();
36375         this.panels.removeKey(panelId);
36376         if(preservePanel){
36377             document.body.appendChild(panel.getEl().dom);
36378         }
36379         if(this.tabs){
36380             this.tabs.removeTab(panel.getEl().id);
36381         }else if (!preservePanel){
36382             this.bodyEl.dom.removeChild(panel.getEl().dom);
36383         }
36384         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36385             var p = this.panels.first();
36386             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36387             tempEl.appendChild(p.getEl().dom);
36388             this.bodyEl.update("");
36389             this.bodyEl.dom.appendChild(p.getEl().dom);
36390             tempEl = null;
36391             this.updateTitle(p.getTitle());
36392             this.tabs = null;
36393             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36394             this.setActivePanel(p);
36395         }
36396         panel.setRegion(null);
36397         if(this.activePanel == panel){
36398             this.activePanel = null;
36399         }
36400         if(this.config.autoDestroy !== false && preservePanel !== true){
36401             try{panel.destroy();}catch(e){}
36402         }
36403         this.fireEvent("panelremoved", this, panel);
36404         return panel;
36405     },
36406
36407     /**
36408      * Returns the TabPanel component used by this region
36409      * @return {Roo.TabPanel}
36410      */
36411     getTabs : function(){
36412         return this.tabs;
36413     },
36414
36415     createTool : function(parentEl, className){
36416         var btn = Roo.DomHelper.append(parentEl, {
36417             tag: "div",
36418             cls: "x-layout-tools-button",
36419             children: [ {
36420                 tag: "div",
36421                 cls: "roo-layout-tools-button-inner " + className,
36422                 html: "&#160;"
36423             }]
36424         }, true);
36425         btn.addClassOnOver("roo-layout-tools-button-over");
36426         return btn;
36427     }
36428 });/*
36429  * Based on:
36430  * Ext JS Library 1.1.1
36431  * Copyright(c) 2006-2007, Ext JS, LLC.
36432  *
36433  * Originally Released Under LGPL - original licence link has changed is not relivant.
36434  *
36435  * Fork - LGPL
36436  * <script type="text/javascript">
36437  */
36438  
36439
36440
36441 /**
36442  * @class Roo.SplitLayoutRegion
36443  * @extends Roo.LayoutRegion
36444  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36445  */
36446 Roo.bootstrap.layout.Split = function(config){
36447     this.cursor = config.cursor;
36448     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36449 };
36450
36451 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36452 {
36453     splitTip : "Drag to resize.",
36454     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36455     useSplitTips : false,
36456
36457     applyConfig : function(config){
36458         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36459     },
36460     
36461     onRender : function(ctr,pos) {
36462         
36463         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36464         if(!this.config.split){
36465             return;
36466         }
36467         if(!this.split){
36468             
36469             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36470                             tag: "div",
36471                             id: this.el.id + "-split",
36472                             cls: "roo-layout-split roo-layout-split-"+this.position,
36473                             html: "&#160;"
36474             });
36475             /** The SplitBar for this region 
36476             * @type Roo.SplitBar */
36477             // does not exist yet...
36478             Roo.log([this.position, this.orientation]);
36479             
36480             this.split = new Roo.bootstrap.SplitBar({
36481                 dragElement : splitEl,
36482                 resizingElement: this.el,
36483                 orientation : this.orientation
36484             });
36485             
36486             this.split.on("moved", this.onSplitMove, this);
36487             this.split.useShim = this.config.useShim === true;
36488             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36489             if(this.useSplitTips){
36490                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36491             }
36492             //if(config.collapsible){
36493             //    this.split.el.on("dblclick", this.collapse,  this);
36494             //}
36495         }
36496         if(typeof this.config.minSize != "undefined"){
36497             this.split.minSize = this.config.minSize;
36498         }
36499         if(typeof this.config.maxSize != "undefined"){
36500             this.split.maxSize = this.config.maxSize;
36501         }
36502         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36503             this.hideSplitter();
36504         }
36505         
36506     },
36507
36508     getHMaxSize : function(){
36509          var cmax = this.config.maxSize || 10000;
36510          var center = this.mgr.getRegion("center");
36511          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36512     },
36513
36514     getVMaxSize : function(){
36515          var cmax = this.config.maxSize || 10000;
36516          var center = this.mgr.getRegion("center");
36517          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36518     },
36519
36520     onSplitMove : function(split, newSize){
36521         this.fireEvent("resized", this, newSize);
36522     },
36523     
36524     /** 
36525      * Returns the {@link Roo.SplitBar} for this region.
36526      * @return {Roo.SplitBar}
36527      */
36528     getSplitBar : function(){
36529         return this.split;
36530     },
36531     
36532     hide : function(){
36533         this.hideSplitter();
36534         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36535     },
36536
36537     hideSplitter : function(){
36538         if(this.split){
36539             this.split.el.setLocation(-2000,-2000);
36540             this.split.el.hide();
36541         }
36542     },
36543
36544     show : function(){
36545         if(this.split){
36546             this.split.el.show();
36547         }
36548         Roo.bootstrap.layout.Split.superclass.show.call(this);
36549     },
36550     
36551     beforeSlide: function(){
36552         if(Roo.isGecko){// firefox overflow auto bug workaround
36553             this.bodyEl.clip();
36554             if(this.tabs) {
36555                 this.tabs.bodyEl.clip();
36556             }
36557             if(this.activePanel){
36558                 this.activePanel.getEl().clip();
36559                 
36560                 if(this.activePanel.beforeSlide){
36561                     this.activePanel.beforeSlide();
36562                 }
36563             }
36564         }
36565     },
36566     
36567     afterSlide : function(){
36568         if(Roo.isGecko){// firefox overflow auto bug workaround
36569             this.bodyEl.unclip();
36570             if(this.tabs) {
36571                 this.tabs.bodyEl.unclip();
36572             }
36573             if(this.activePanel){
36574                 this.activePanel.getEl().unclip();
36575                 if(this.activePanel.afterSlide){
36576                     this.activePanel.afterSlide();
36577                 }
36578             }
36579         }
36580     },
36581
36582     initAutoHide : function(){
36583         if(this.autoHide !== false){
36584             if(!this.autoHideHd){
36585                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36586                 this.autoHideHd = {
36587                     "mouseout": function(e){
36588                         if(!e.within(this.el, true)){
36589                             st.delay(500);
36590                         }
36591                     },
36592                     "mouseover" : function(e){
36593                         st.cancel();
36594                     },
36595                     scope : this
36596                 };
36597             }
36598             this.el.on(this.autoHideHd);
36599         }
36600     },
36601
36602     clearAutoHide : function(){
36603         if(this.autoHide !== false){
36604             this.el.un("mouseout", this.autoHideHd.mouseout);
36605             this.el.un("mouseover", this.autoHideHd.mouseover);
36606         }
36607     },
36608
36609     clearMonitor : function(){
36610         Roo.get(document).un("click", this.slideInIf, this);
36611     },
36612
36613     // these names are backwards but not changed for compat
36614     slideOut : function(){
36615         if(this.isSlid || this.el.hasActiveFx()){
36616             return;
36617         }
36618         this.isSlid = true;
36619         if(this.collapseBtn){
36620             this.collapseBtn.hide();
36621         }
36622         this.closeBtnState = this.closeBtn.getStyle('display');
36623         this.closeBtn.hide();
36624         if(this.stickBtn){
36625             this.stickBtn.show();
36626         }
36627         this.el.show();
36628         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36629         this.beforeSlide();
36630         this.el.setStyle("z-index", 10001);
36631         this.el.slideIn(this.getSlideAnchor(), {
36632             callback: function(){
36633                 this.afterSlide();
36634                 this.initAutoHide();
36635                 Roo.get(document).on("click", this.slideInIf, this);
36636                 this.fireEvent("slideshow", this);
36637             },
36638             scope: this,
36639             block: true
36640         });
36641     },
36642
36643     afterSlideIn : function(){
36644         this.clearAutoHide();
36645         this.isSlid = false;
36646         this.clearMonitor();
36647         this.el.setStyle("z-index", "");
36648         if(this.collapseBtn){
36649             this.collapseBtn.show();
36650         }
36651         this.closeBtn.setStyle('display', this.closeBtnState);
36652         if(this.stickBtn){
36653             this.stickBtn.hide();
36654         }
36655         this.fireEvent("slidehide", this);
36656     },
36657
36658     slideIn : function(cb){
36659         if(!this.isSlid || this.el.hasActiveFx()){
36660             Roo.callback(cb);
36661             return;
36662         }
36663         this.isSlid = false;
36664         this.beforeSlide();
36665         this.el.slideOut(this.getSlideAnchor(), {
36666             callback: function(){
36667                 this.el.setLeftTop(-10000, -10000);
36668                 this.afterSlide();
36669                 this.afterSlideIn();
36670                 Roo.callback(cb);
36671             },
36672             scope: this,
36673             block: true
36674         });
36675     },
36676     
36677     slideInIf : function(e){
36678         if(!e.within(this.el)){
36679             this.slideIn();
36680         }
36681     },
36682
36683     animateCollapse : function(){
36684         this.beforeSlide();
36685         this.el.setStyle("z-index", 20000);
36686         var anchor = this.getSlideAnchor();
36687         this.el.slideOut(anchor, {
36688             callback : function(){
36689                 this.el.setStyle("z-index", "");
36690                 this.collapsedEl.slideIn(anchor, {duration:.3});
36691                 this.afterSlide();
36692                 this.el.setLocation(-10000,-10000);
36693                 this.el.hide();
36694                 this.fireEvent("collapsed", this);
36695             },
36696             scope: this,
36697             block: true
36698         });
36699     },
36700
36701     animateExpand : function(){
36702         this.beforeSlide();
36703         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36704         this.el.setStyle("z-index", 20000);
36705         this.collapsedEl.hide({
36706             duration:.1
36707         });
36708         this.el.slideIn(this.getSlideAnchor(), {
36709             callback : function(){
36710                 this.el.setStyle("z-index", "");
36711                 this.afterSlide();
36712                 if(this.split){
36713                     this.split.el.show();
36714                 }
36715                 this.fireEvent("invalidated", this);
36716                 this.fireEvent("expanded", this);
36717             },
36718             scope: this,
36719             block: true
36720         });
36721     },
36722
36723     anchors : {
36724         "west" : "left",
36725         "east" : "right",
36726         "north" : "top",
36727         "south" : "bottom"
36728     },
36729
36730     sanchors : {
36731         "west" : "l",
36732         "east" : "r",
36733         "north" : "t",
36734         "south" : "b"
36735     },
36736
36737     canchors : {
36738         "west" : "tl-tr",
36739         "east" : "tr-tl",
36740         "north" : "tl-bl",
36741         "south" : "bl-tl"
36742     },
36743
36744     getAnchor : function(){
36745         return this.anchors[this.position];
36746     },
36747
36748     getCollapseAnchor : function(){
36749         return this.canchors[this.position];
36750     },
36751
36752     getSlideAnchor : function(){
36753         return this.sanchors[this.position];
36754     },
36755
36756     getAlignAdj : function(){
36757         var cm = this.cmargins;
36758         switch(this.position){
36759             case "west":
36760                 return [0, 0];
36761             break;
36762             case "east":
36763                 return [0, 0];
36764             break;
36765             case "north":
36766                 return [0, 0];
36767             break;
36768             case "south":
36769                 return [0, 0];
36770             break;
36771         }
36772     },
36773
36774     getExpandAdj : function(){
36775         var c = this.collapsedEl, cm = this.cmargins;
36776         switch(this.position){
36777             case "west":
36778                 return [-(cm.right+c.getWidth()+cm.left), 0];
36779             break;
36780             case "east":
36781                 return [cm.right+c.getWidth()+cm.left, 0];
36782             break;
36783             case "north":
36784                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36785             break;
36786             case "south":
36787                 return [0, cm.top+cm.bottom+c.getHeight()];
36788             break;
36789         }
36790     }
36791 });/*
36792  * Based on:
36793  * Ext JS Library 1.1.1
36794  * Copyright(c) 2006-2007, Ext JS, LLC.
36795  *
36796  * Originally Released Under LGPL - original licence link has changed is not relivant.
36797  *
36798  * Fork - LGPL
36799  * <script type="text/javascript">
36800  */
36801 /*
36802  * These classes are private internal classes
36803  */
36804 Roo.bootstrap.layout.Center = function(config){
36805     config.region = "center";
36806     Roo.bootstrap.layout.Region.call(this, config);
36807     this.visible = true;
36808     this.minWidth = config.minWidth || 20;
36809     this.minHeight = config.minHeight || 20;
36810 };
36811
36812 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36813     hide : function(){
36814         // center panel can't be hidden
36815     },
36816     
36817     show : function(){
36818         // center panel can't be hidden
36819     },
36820     
36821     getMinWidth: function(){
36822         return this.minWidth;
36823     },
36824     
36825     getMinHeight: function(){
36826         return this.minHeight;
36827     }
36828 });
36829
36830
36831
36832
36833  
36834
36835
36836
36837
36838
36839 Roo.bootstrap.layout.North = function(config)
36840 {
36841     config.region = 'north';
36842     config.cursor = 'n-resize';
36843     
36844     Roo.bootstrap.layout.Split.call(this, config);
36845     
36846     
36847     if(this.split){
36848         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36849         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36850         this.split.el.addClass("roo-layout-split-v");
36851     }
36852     var size = config.initialSize || config.height;
36853     if(typeof size != "undefined"){
36854         this.el.setHeight(size);
36855     }
36856 };
36857 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36858 {
36859     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36860     
36861     
36862     
36863     getBox : function(){
36864         if(this.collapsed){
36865             return this.collapsedEl.getBox();
36866         }
36867         var box = this.el.getBox();
36868         if(this.split){
36869             box.height += this.split.el.getHeight();
36870         }
36871         return box;
36872     },
36873     
36874     updateBox : function(box){
36875         if(this.split && !this.collapsed){
36876             box.height -= this.split.el.getHeight();
36877             this.split.el.setLeft(box.x);
36878             this.split.el.setTop(box.y+box.height);
36879             this.split.el.setWidth(box.width);
36880         }
36881         if(this.collapsed){
36882             this.updateBody(box.width, null);
36883         }
36884         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36885     }
36886 });
36887
36888
36889
36890
36891
36892 Roo.bootstrap.layout.South = function(config){
36893     config.region = 'south';
36894     config.cursor = 's-resize';
36895     Roo.bootstrap.layout.Split.call(this, config);
36896     if(this.split){
36897         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36898         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36899         this.split.el.addClass("roo-layout-split-v");
36900     }
36901     var size = config.initialSize || config.height;
36902     if(typeof size != "undefined"){
36903         this.el.setHeight(size);
36904     }
36905 };
36906
36907 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36908     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36909     getBox : function(){
36910         if(this.collapsed){
36911             return this.collapsedEl.getBox();
36912         }
36913         var box = this.el.getBox();
36914         if(this.split){
36915             var sh = this.split.el.getHeight();
36916             box.height += sh;
36917             box.y -= sh;
36918         }
36919         return box;
36920     },
36921     
36922     updateBox : function(box){
36923         if(this.split && !this.collapsed){
36924             var sh = this.split.el.getHeight();
36925             box.height -= sh;
36926             box.y += sh;
36927             this.split.el.setLeft(box.x);
36928             this.split.el.setTop(box.y-sh);
36929             this.split.el.setWidth(box.width);
36930         }
36931         if(this.collapsed){
36932             this.updateBody(box.width, null);
36933         }
36934         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36935     }
36936 });
36937
36938 Roo.bootstrap.layout.East = function(config){
36939     config.region = "east";
36940     config.cursor = "e-resize";
36941     Roo.bootstrap.layout.Split.call(this, config);
36942     if(this.split){
36943         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36944         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36945         this.split.el.addClass("roo-layout-split-h");
36946     }
36947     var size = config.initialSize || config.width;
36948     if(typeof size != "undefined"){
36949         this.el.setWidth(size);
36950     }
36951 };
36952 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36953     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36954     getBox : function(){
36955         if(this.collapsed){
36956             return this.collapsedEl.getBox();
36957         }
36958         var box = this.el.getBox();
36959         if(this.split){
36960             var sw = this.split.el.getWidth();
36961             box.width += sw;
36962             box.x -= sw;
36963         }
36964         return box;
36965     },
36966
36967     updateBox : function(box){
36968         if(this.split && !this.collapsed){
36969             var sw = this.split.el.getWidth();
36970             box.width -= sw;
36971             this.split.el.setLeft(box.x);
36972             this.split.el.setTop(box.y);
36973             this.split.el.setHeight(box.height);
36974             box.x += sw;
36975         }
36976         if(this.collapsed){
36977             this.updateBody(null, box.height);
36978         }
36979         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36980     }
36981 });
36982
36983 Roo.bootstrap.layout.West = function(config){
36984     config.region = "west";
36985     config.cursor = "w-resize";
36986     
36987     Roo.bootstrap.layout.Split.call(this, config);
36988     if(this.split){
36989         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36990         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36991         this.split.el.addClass("roo-layout-split-h");
36992     }
36993     
36994 };
36995 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36996     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36997     
36998     onRender: function(ctr, pos)
36999     {
37000         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37001         var size = this.config.initialSize || this.config.width;
37002         if(typeof size != "undefined"){
37003             this.el.setWidth(size);
37004         }
37005     },
37006     
37007     getBox : function(){
37008         if(this.collapsed){
37009             return this.collapsedEl.getBox();
37010         }
37011         var box = this.el.getBox();
37012         if(this.split){
37013             box.width += this.split.el.getWidth();
37014         }
37015         return box;
37016     },
37017     
37018     updateBox : function(box){
37019         if(this.split && !this.collapsed){
37020             var sw = this.split.el.getWidth();
37021             box.width -= sw;
37022             this.split.el.setLeft(box.x+box.width);
37023             this.split.el.setTop(box.y);
37024             this.split.el.setHeight(box.height);
37025         }
37026         if(this.collapsed){
37027             this.updateBody(null, box.height);
37028         }
37029         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37030     }
37031 });
37032 Roo.namespace("Roo.bootstrap.panel");/*
37033  * Based on:
37034  * Ext JS Library 1.1.1
37035  * Copyright(c) 2006-2007, Ext JS, LLC.
37036  *
37037  * Originally Released Under LGPL - original licence link has changed is not relivant.
37038  *
37039  * Fork - LGPL
37040  * <script type="text/javascript">
37041  */
37042 /**
37043  * @class Roo.ContentPanel
37044  * @extends Roo.util.Observable
37045  * A basic ContentPanel element.
37046  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37047  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37048  * @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
37049  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37050  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37051  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37052  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37053  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37054  * @cfg {String} title          The title for this panel
37055  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37056  * @cfg {String} url            Calls {@link #setUrl} with this value
37057  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37058  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37059  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37060  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37061  * @cfg {Boolean} badges render the badges
37062
37063  * @constructor
37064  * Create a new ContentPanel.
37065  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37066  * @param {String/Object} config A string to set only the title or a config object
37067  * @param {String} content (optional) Set the HTML content for this panel
37068  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37069  */
37070 Roo.bootstrap.panel.Content = function( config){
37071     
37072     this.tpl = config.tpl || false;
37073     
37074     var el = config.el;
37075     var content = config.content;
37076
37077     if(config.autoCreate){ // xtype is available if this is called from factory
37078         el = Roo.id();
37079     }
37080     this.el = Roo.get(el);
37081     if(!this.el && config && config.autoCreate){
37082         if(typeof config.autoCreate == "object"){
37083             if(!config.autoCreate.id){
37084                 config.autoCreate.id = config.id||el;
37085             }
37086             this.el = Roo.DomHelper.append(document.body,
37087                         config.autoCreate, true);
37088         }else{
37089             var elcfg =  {   tag: "div",
37090                             cls: "roo-layout-inactive-content",
37091                             id: config.id||el
37092                             };
37093             if (config.html) {
37094                 elcfg.html = config.html;
37095                 
37096             }
37097                         
37098             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37099         }
37100     } 
37101     this.closable = false;
37102     this.loaded = false;
37103     this.active = false;
37104    
37105       
37106     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37107         
37108         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37109         
37110         this.wrapEl = this.el; //this.el.wrap();
37111         var ti = [];
37112         if (config.toolbar.items) {
37113             ti = config.toolbar.items ;
37114             delete config.toolbar.items ;
37115         }
37116         
37117         var nitems = [];
37118         this.toolbar.render(this.wrapEl, 'before');
37119         for(var i =0;i < ti.length;i++) {
37120           //  Roo.log(['add child', items[i]]);
37121             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37122         }
37123         this.toolbar.items = nitems;
37124         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37125         delete config.toolbar;
37126         
37127     }
37128     /*
37129     // xtype created footer. - not sure if will work as we normally have to render first..
37130     if (this.footer && !this.footer.el && this.footer.xtype) {
37131         if (!this.wrapEl) {
37132             this.wrapEl = this.el.wrap();
37133         }
37134     
37135         this.footer.container = this.wrapEl.createChild();
37136          
37137         this.footer = Roo.factory(this.footer, Roo);
37138         
37139     }
37140     */
37141     
37142      if(typeof config == "string"){
37143         this.title = config;
37144     }else{
37145         Roo.apply(this, config);
37146     }
37147     
37148     if(this.resizeEl){
37149         this.resizeEl = Roo.get(this.resizeEl, true);
37150     }else{
37151         this.resizeEl = this.el;
37152     }
37153     // handle view.xtype
37154     
37155  
37156     
37157     
37158     this.addEvents({
37159         /**
37160          * @event activate
37161          * Fires when this panel is activated. 
37162          * @param {Roo.ContentPanel} this
37163          */
37164         "activate" : true,
37165         /**
37166          * @event deactivate
37167          * Fires when this panel is activated. 
37168          * @param {Roo.ContentPanel} this
37169          */
37170         "deactivate" : true,
37171
37172         /**
37173          * @event resize
37174          * Fires when this panel is resized if fitToFrame is true.
37175          * @param {Roo.ContentPanel} this
37176          * @param {Number} width The width after any component adjustments
37177          * @param {Number} height The height after any component adjustments
37178          */
37179         "resize" : true,
37180         
37181          /**
37182          * @event render
37183          * Fires when this tab is created
37184          * @param {Roo.ContentPanel} this
37185          */
37186         "render" : true
37187         
37188         
37189         
37190     });
37191     
37192
37193     
37194     
37195     if(this.autoScroll){
37196         this.resizeEl.setStyle("overflow", "auto");
37197     } else {
37198         // fix randome scrolling
37199         //this.el.on('scroll', function() {
37200         //    Roo.log('fix random scolling');
37201         //    this.scrollTo('top',0); 
37202         //});
37203     }
37204     content = content || this.content;
37205     if(content){
37206         this.setContent(content);
37207     }
37208     if(config && config.url){
37209         this.setUrl(this.url, this.params, this.loadOnce);
37210     }
37211     
37212     
37213     
37214     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37215     
37216     if (this.view && typeof(this.view.xtype) != 'undefined') {
37217         this.view.el = this.el.appendChild(document.createElement("div"));
37218         this.view = Roo.factory(this.view); 
37219         this.view.render  &&  this.view.render(false, '');  
37220     }
37221     
37222     
37223     this.fireEvent('render', this);
37224 };
37225
37226 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37227     
37228     tabTip : '',
37229     
37230     setRegion : function(region){
37231         this.region = region;
37232         this.setActiveClass(region && !this.background);
37233     },
37234     
37235     
37236     setActiveClass: function(state)
37237     {
37238         if(state){
37239            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37240            this.el.setStyle('position','relative');
37241         }else{
37242            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37243            this.el.setStyle('position', 'absolute');
37244         } 
37245     },
37246     
37247     /**
37248      * Returns the toolbar for this Panel if one was configured. 
37249      * @return {Roo.Toolbar} 
37250      */
37251     getToolbar : function(){
37252         return this.toolbar;
37253     },
37254     
37255     setActiveState : function(active)
37256     {
37257         this.active = active;
37258         this.setActiveClass(active);
37259         if(!active){
37260             if(this.fireEvent("deactivate", this) === false){
37261                 return false;
37262             }
37263             return true;
37264         }
37265         this.fireEvent("activate", this);
37266         return true;
37267     },
37268     /**
37269      * Updates this panel's element
37270      * @param {String} content The new content
37271      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37272     */
37273     setContent : function(content, loadScripts){
37274         this.el.update(content, loadScripts);
37275     },
37276
37277     ignoreResize : function(w, h){
37278         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37279             return true;
37280         }else{
37281             this.lastSize = {width: w, height: h};
37282             return false;
37283         }
37284     },
37285     /**
37286      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37287      * @return {Roo.UpdateManager} The UpdateManager
37288      */
37289     getUpdateManager : function(){
37290         return this.el.getUpdateManager();
37291     },
37292      /**
37293      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37294      * @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:
37295 <pre><code>
37296 panel.load({
37297     url: "your-url.php",
37298     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37299     callback: yourFunction,
37300     scope: yourObject, //(optional scope)
37301     discardUrl: false,
37302     nocache: false,
37303     text: "Loading...",
37304     timeout: 30,
37305     scripts: false
37306 });
37307 </code></pre>
37308      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37309      * 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.
37310      * @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}
37311      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37312      * @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.
37313      * @return {Roo.ContentPanel} this
37314      */
37315     load : function(){
37316         var um = this.el.getUpdateManager();
37317         um.update.apply(um, arguments);
37318         return this;
37319     },
37320
37321
37322     /**
37323      * 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.
37324      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37325      * @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)
37326      * @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)
37327      * @return {Roo.UpdateManager} The UpdateManager
37328      */
37329     setUrl : function(url, params, loadOnce){
37330         if(this.refreshDelegate){
37331             this.removeListener("activate", this.refreshDelegate);
37332         }
37333         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37334         this.on("activate", this.refreshDelegate);
37335         return this.el.getUpdateManager();
37336     },
37337     
37338     _handleRefresh : function(url, params, loadOnce){
37339         if(!loadOnce || !this.loaded){
37340             var updater = this.el.getUpdateManager();
37341             updater.update(url, params, this._setLoaded.createDelegate(this));
37342         }
37343     },
37344     
37345     _setLoaded : function(){
37346         this.loaded = true;
37347     }, 
37348     
37349     /**
37350      * Returns this panel's id
37351      * @return {String} 
37352      */
37353     getId : function(){
37354         return this.el.id;
37355     },
37356     
37357     /** 
37358      * Returns this panel's element - used by regiosn to add.
37359      * @return {Roo.Element} 
37360      */
37361     getEl : function(){
37362         return this.wrapEl || this.el;
37363     },
37364     
37365    
37366     
37367     adjustForComponents : function(width, height)
37368     {
37369         //Roo.log('adjustForComponents ');
37370         if(this.resizeEl != this.el){
37371             width -= this.el.getFrameWidth('lr');
37372             height -= this.el.getFrameWidth('tb');
37373         }
37374         if(this.toolbar){
37375             var te = this.toolbar.getEl();
37376             te.setWidth(width);
37377             height -= te.getHeight();
37378         }
37379         if(this.footer){
37380             var te = this.footer.getEl();
37381             te.setWidth(width);
37382             height -= te.getHeight();
37383         }
37384         
37385         
37386         if(this.adjustments){
37387             width += this.adjustments[0];
37388             height += this.adjustments[1];
37389         }
37390         return {"width": width, "height": height};
37391     },
37392     
37393     setSize : function(width, height){
37394         if(this.fitToFrame && !this.ignoreResize(width, height)){
37395             if(this.fitContainer && this.resizeEl != this.el){
37396                 this.el.setSize(width, height);
37397             }
37398             var size = this.adjustForComponents(width, height);
37399             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37400             this.fireEvent('resize', this, size.width, size.height);
37401         }
37402     },
37403     
37404     /**
37405      * Returns this panel's title
37406      * @return {String} 
37407      */
37408     getTitle : function(){
37409         
37410         if (typeof(this.title) != 'object') {
37411             return this.title;
37412         }
37413         
37414         var t = '';
37415         for (var k in this.title) {
37416             if (!this.title.hasOwnProperty(k)) {
37417                 continue;
37418             }
37419             
37420             if (k.indexOf('-') >= 0) {
37421                 var s = k.split('-');
37422                 for (var i = 0; i<s.length; i++) {
37423                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37424                 }
37425             } else {
37426                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37427             }
37428         }
37429         return t;
37430     },
37431     
37432     /**
37433      * Set this panel's title
37434      * @param {String} title
37435      */
37436     setTitle : function(title){
37437         this.title = title;
37438         if(this.region){
37439             this.region.updatePanelTitle(this, title);
37440         }
37441     },
37442     
37443     /**
37444      * Returns true is this panel was configured to be closable
37445      * @return {Boolean} 
37446      */
37447     isClosable : function(){
37448         return this.closable;
37449     },
37450     
37451     beforeSlide : function(){
37452         this.el.clip();
37453         this.resizeEl.clip();
37454     },
37455     
37456     afterSlide : function(){
37457         this.el.unclip();
37458         this.resizeEl.unclip();
37459     },
37460     
37461     /**
37462      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37463      *   Will fail silently if the {@link #setUrl} method has not been called.
37464      *   This does not activate the panel, just updates its content.
37465      */
37466     refresh : function(){
37467         if(this.refreshDelegate){
37468            this.loaded = false;
37469            this.refreshDelegate();
37470         }
37471     },
37472     
37473     /**
37474      * Destroys this panel
37475      */
37476     destroy : function(){
37477         this.el.removeAllListeners();
37478         var tempEl = document.createElement("span");
37479         tempEl.appendChild(this.el.dom);
37480         tempEl.innerHTML = "";
37481         this.el.remove();
37482         this.el = null;
37483     },
37484     
37485     /**
37486      * form - if the content panel contains a form - this is a reference to it.
37487      * @type {Roo.form.Form}
37488      */
37489     form : false,
37490     /**
37491      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37492      *    This contains a reference to it.
37493      * @type {Roo.View}
37494      */
37495     view : false,
37496     
37497       /**
37498      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37499      * <pre><code>
37500
37501 layout.addxtype({
37502        xtype : 'Form',
37503        items: [ .... ]
37504    }
37505 );
37506
37507 </code></pre>
37508      * @param {Object} cfg Xtype definition of item to add.
37509      */
37510     
37511     
37512     getChildContainer: function () {
37513         return this.getEl();
37514     }
37515     
37516     
37517     /*
37518         var  ret = new Roo.factory(cfg);
37519         return ret;
37520         
37521         
37522         // add form..
37523         if (cfg.xtype.match(/^Form$/)) {
37524             
37525             var el;
37526             //if (this.footer) {
37527             //    el = this.footer.container.insertSibling(false, 'before');
37528             //} else {
37529                 el = this.el.createChild();
37530             //}
37531
37532             this.form = new  Roo.form.Form(cfg);
37533             
37534             
37535             if ( this.form.allItems.length) {
37536                 this.form.render(el.dom);
37537             }
37538             return this.form;
37539         }
37540         // should only have one of theses..
37541         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37542             // views.. should not be just added - used named prop 'view''
37543             
37544             cfg.el = this.el.appendChild(document.createElement("div"));
37545             // factory?
37546             
37547             var ret = new Roo.factory(cfg);
37548              
37549              ret.render && ret.render(false, ''); // render blank..
37550             this.view = ret;
37551             return ret;
37552         }
37553         return false;
37554     }
37555     \*/
37556 });
37557  
37558 /**
37559  * @class Roo.bootstrap.panel.Grid
37560  * @extends Roo.bootstrap.panel.Content
37561  * @constructor
37562  * Create a new GridPanel.
37563  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37564  * @param {Object} config A the config object
37565   
37566  */
37567
37568
37569
37570 Roo.bootstrap.panel.Grid = function(config)
37571 {
37572     
37573       
37574     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37575         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37576
37577     config.el = this.wrapper;
37578     //this.el = this.wrapper;
37579     
37580       if (config.container) {
37581         // ctor'ed from a Border/panel.grid
37582         
37583         
37584         this.wrapper.setStyle("overflow", "hidden");
37585         this.wrapper.addClass('roo-grid-container');
37586
37587     }
37588     
37589     
37590     if(config.toolbar){
37591         var tool_el = this.wrapper.createChild();    
37592         this.toolbar = Roo.factory(config.toolbar);
37593         var ti = [];
37594         if (config.toolbar.items) {
37595             ti = config.toolbar.items ;
37596             delete config.toolbar.items ;
37597         }
37598         
37599         var nitems = [];
37600         this.toolbar.render(tool_el);
37601         for(var i =0;i < ti.length;i++) {
37602           //  Roo.log(['add child', items[i]]);
37603             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37604         }
37605         this.toolbar.items = nitems;
37606         
37607         delete config.toolbar;
37608     }
37609     
37610     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37611     config.grid.scrollBody = true;;
37612     config.grid.monitorWindowResize = false; // turn off autosizing
37613     config.grid.autoHeight = false;
37614     config.grid.autoWidth = false;
37615     
37616     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37617     
37618     if (config.background) {
37619         // render grid on panel activation (if panel background)
37620         this.on('activate', function(gp) {
37621             if (!gp.grid.rendered) {
37622                 gp.grid.render(this.wrapper);
37623                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37624             }
37625         });
37626             
37627     } else {
37628         this.grid.render(this.wrapper);
37629         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37630
37631     }
37632     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37633     // ??? needed ??? config.el = this.wrapper;
37634     
37635     
37636     
37637   
37638     // xtype created footer. - not sure if will work as we normally have to render first..
37639     if (this.footer && !this.footer.el && this.footer.xtype) {
37640         
37641         var ctr = this.grid.getView().getFooterPanel(true);
37642         this.footer.dataSource = this.grid.dataSource;
37643         this.footer = Roo.factory(this.footer, Roo);
37644         this.footer.render(ctr);
37645         
37646     }
37647     
37648     
37649     
37650     
37651      
37652 };
37653
37654 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37655     getId : function(){
37656         return this.grid.id;
37657     },
37658     
37659     /**
37660      * Returns the grid for this panel
37661      * @return {Roo.bootstrap.Table} 
37662      */
37663     getGrid : function(){
37664         return this.grid;    
37665     },
37666     
37667     setSize : function(width, height){
37668         if(!this.ignoreResize(width, height)){
37669             var grid = this.grid;
37670             var size = this.adjustForComponents(width, height);
37671             var gridel = grid.getGridEl();
37672             gridel.setSize(size.width, size.height);
37673             /*
37674             var thd = grid.getGridEl().select('thead',true).first();
37675             var tbd = grid.getGridEl().select('tbody', true).first();
37676             if (tbd) {
37677                 tbd.setSize(width, height - thd.getHeight());
37678             }
37679             */
37680             grid.autoSize();
37681         }
37682     },
37683      
37684     
37685     
37686     beforeSlide : function(){
37687         this.grid.getView().scroller.clip();
37688     },
37689     
37690     afterSlide : function(){
37691         this.grid.getView().scroller.unclip();
37692     },
37693     
37694     destroy : function(){
37695         this.grid.destroy();
37696         delete this.grid;
37697         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37698     }
37699 });
37700
37701 /**
37702  * @class Roo.bootstrap.panel.Nest
37703  * @extends Roo.bootstrap.panel.Content
37704  * @constructor
37705  * Create a new Panel, that can contain a layout.Border.
37706  * 
37707  * 
37708  * @param {Roo.BorderLayout} layout The layout for this panel
37709  * @param {String/Object} config A string to set only the title or a config object
37710  */
37711 Roo.bootstrap.panel.Nest = function(config)
37712 {
37713     // construct with only one argument..
37714     /* FIXME - implement nicer consturctors
37715     if (layout.layout) {
37716         config = layout;
37717         layout = config.layout;
37718         delete config.layout;
37719     }
37720     if (layout.xtype && !layout.getEl) {
37721         // then layout needs constructing..
37722         layout = Roo.factory(layout, Roo);
37723     }
37724     */
37725     
37726     config.el =  config.layout.getEl();
37727     
37728     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37729     
37730     config.layout.monitorWindowResize = false; // turn off autosizing
37731     this.layout = config.layout;
37732     this.layout.getEl().addClass("roo-layout-nested-layout");
37733     
37734     
37735     
37736     
37737 };
37738
37739 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37740
37741     setSize : function(width, height){
37742         if(!this.ignoreResize(width, height)){
37743             var size = this.adjustForComponents(width, height);
37744             var el = this.layout.getEl();
37745             if (size.height < 1) {
37746                 el.setWidth(size.width);   
37747             } else {
37748                 el.setSize(size.width, size.height);
37749             }
37750             var touch = el.dom.offsetWidth;
37751             this.layout.layout();
37752             // ie requires a double layout on the first pass
37753             if(Roo.isIE && !this.initialized){
37754                 this.initialized = true;
37755                 this.layout.layout();
37756             }
37757         }
37758     },
37759     
37760     // activate all subpanels if not currently active..
37761     
37762     setActiveState : function(active){
37763         this.active = active;
37764         this.setActiveClass(active);
37765         
37766         if(!active){
37767             this.fireEvent("deactivate", this);
37768             return;
37769         }
37770         
37771         this.fireEvent("activate", this);
37772         // not sure if this should happen before or after..
37773         if (!this.layout) {
37774             return; // should not happen..
37775         }
37776         var reg = false;
37777         for (var r in this.layout.regions) {
37778             reg = this.layout.getRegion(r);
37779             if (reg.getActivePanel()) {
37780                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37781                 reg.setActivePanel(reg.getActivePanel());
37782                 continue;
37783             }
37784             if (!reg.panels.length) {
37785                 continue;
37786             }
37787             reg.showPanel(reg.getPanel(0));
37788         }
37789         
37790         
37791         
37792         
37793     },
37794     
37795     /**
37796      * Returns the nested BorderLayout for this panel
37797      * @return {Roo.BorderLayout} 
37798      */
37799     getLayout : function(){
37800         return this.layout;
37801     },
37802     
37803      /**
37804      * Adds a xtype elements to the layout of the nested panel
37805      * <pre><code>
37806
37807 panel.addxtype({
37808        xtype : 'ContentPanel',
37809        region: 'west',
37810        items: [ .... ]
37811    }
37812 );
37813
37814 panel.addxtype({
37815         xtype : 'NestedLayoutPanel',
37816         region: 'west',
37817         layout: {
37818            center: { },
37819            west: { }   
37820         },
37821         items : [ ... list of content panels or nested layout panels.. ]
37822    }
37823 );
37824 </code></pre>
37825      * @param {Object} cfg Xtype definition of item to add.
37826      */
37827     addxtype : function(cfg) {
37828         return this.layout.addxtype(cfg);
37829     
37830     }
37831 });        /*
37832  * Based on:
37833  * Ext JS Library 1.1.1
37834  * Copyright(c) 2006-2007, Ext JS, LLC.
37835  *
37836  * Originally Released Under LGPL - original licence link has changed is not relivant.
37837  *
37838  * Fork - LGPL
37839  * <script type="text/javascript">
37840  */
37841 /**
37842  * @class Roo.TabPanel
37843  * @extends Roo.util.Observable
37844  * A lightweight tab container.
37845  * <br><br>
37846  * Usage:
37847  * <pre><code>
37848 // basic tabs 1, built from existing content
37849 var tabs = new Roo.TabPanel("tabs1");
37850 tabs.addTab("script", "View Script");
37851 tabs.addTab("markup", "View Markup");
37852 tabs.activate("script");
37853
37854 // more advanced tabs, built from javascript
37855 var jtabs = new Roo.TabPanel("jtabs");
37856 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37857
37858 // set up the UpdateManager
37859 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37860 var updater = tab2.getUpdateManager();
37861 updater.setDefaultUrl("ajax1.htm");
37862 tab2.on('activate', updater.refresh, updater, true);
37863
37864 // Use setUrl for Ajax loading
37865 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37866 tab3.setUrl("ajax2.htm", null, true);
37867
37868 // Disabled tab
37869 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37870 tab4.disable();
37871
37872 jtabs.activate("jtabs-1");
37873  * </code></pre>
37874  * @constructor
37875  * Create a new TabPanel.
37876  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37877  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37878  */
37879 Roo.bootstrap.panel.Tabs = function(config){
37880     /**
37881     * The container element for this TabPanel.
37882     * @type Roo.Element
37883     */
37884     this.el = Roo.get(config.el);
37885     delete config.el;
37886     if(config){
37887         if(typeof config == "boolean"){
37888             this.tabPosition = config ? "bottom" : "top";
37889         }else{
37890             Roo.apply(this, config);
37891         }
37892     }
37893     
37894     if(this.tabPosition == "bottom"){
37895         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37896         this.el.addClass("roo-tabs-bottom");
37897     }
37898     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37899     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37900     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37901     if(Roo.isIE){
37902         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37903     }
37904     if(this.tabPosition != "bottom"){
37905         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37906          * @type Roo.Element
37907          */
37908         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37909         this.el.addClass("roo-tabs-top");
37910     }
37911     this.items = [];
37912
37913     this.bodyEl.setStyle("position", "relative");
37914
37915     this.active = null;
37916     this.activateDelegate = this.activate.createDelegate(this);
37917
37918     this.addEvents({
37919         /**
37920          * @event tabchange
37921          * Fires when the active tab changes
37922          * @param {Roo.TabPanel} this
37923          * @param {Roo.TabPanelItem} activePanel The new active tab
37924          */
37925         "tabchange": true,
37926         /**
37927          * @event beforetabchange
37928          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37929          * @param {Roo.TabPanel} this
37930          * @param {Object} e Set cancel to true on this object to cancel the tab change
37931          * @param {Roo.TabPanelItem} tab The tab being changed to
37932          */
37933         "beforetabchange" : true
37934     });
37935
37936     Roo.EventManager.onWindowResize(this.onResize, this);
37937     this.cpad = this.el.getPadding("lr");
37938     this.hiddenCount = 0;
37939
37940
37941     // toolbar on the tabbar support...
37942     if (this.toolbar) {
37943         alert("no toolbar support yet");
37944         this.toolbar  = false;
37945         /*
37946         var tcfg = this.toolbar;
37947         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37948         this.toolbar = new Roo.Toolbar(tcfg);
37949         if (Roo.isSafari) {
37950             var tbl = tcfg.container.child('table', true);
37951             tbl.setAttribute('width', '100%');
37952         }
37953         */
37954         
37955     }
37956    
37957
37958
37959     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37960 };
37961
37962 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37963     /*
37964      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37965      */
37966     tabPosition : "top",
37967     /*
37968      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37969      */
37970     currentTabWidth : 0,
37971     /*
37972      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37973      */
37974     minTabWidth : 40,
37975     /*
37976      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37977      */
37978     maxTabWidth : 250,
37979     /*
37980      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37981      */
37982     preferredTabWidth : 175,
37983     /*
37984      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37985      */
37986     resizeTabs : false,
37987     /*
37988      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37989      */
37990     monitorResize : true,
37991     /*
37992      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37993      */
37994     toolbar : false,
37995
37996     /**
37997      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37998      * @param {String} id The id of the div to use <b>or create</b>
37999      * @param {String} text The text for the tab
38000      * @param {String} content (optional) Content to put in the TabPanelItem body
38001      * @param {Boolean} closable (optional) True to create a close icon on the tab
38002      * @return {Roo.TabPanelItem} The created TabPanelItem
38003      */
38004     addTab : function(id, text, content, closable, tpl)
38005     {
38006         var item = new Roo.bootstrap.panel.TabItem({
38007             panel: this,
38008             id : id,
38009             text : text,
38010             closable : closable,
38011             tpl : tpl
38012         });
38013         this.addTabItem(item);
38014         if(content){
38015             item.setContent(content);
38016         }
38017         return item;
38018     },
38019
38020     /**
38021      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38022      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38023      * @return {Roo.TabPanelItem}
38024      */
38025     getTab : function(id){
38026         return this.items[id];
38027     },
38028
38029     /**
38030      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38031      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38032      */
38033     hideTab : function(id){
38034         var t = this.items[id];
38035         if(!t.isHidden()){
38036            t.setHidden(true);
38037            this.hiddenCount++;
38038            this.autoSizeTabs();
38039         }
38040     },
38041
38042     /**
38043      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38044      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38045      */
38046     unhideTab : function(id){
38047         var t = this.items[id];
38048         if(t.isHidden()){
38049            t.setHidden(false);
38050            this.hiddenCount--;
38051            this.autoSizeTabs();
38052         }
38053     },
38054
38055     /**
38056      * Adds an existing {@link Roo.TabPanelItem}.
38057      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38058      */
38059     addTabItem : function(item){
38060         this.items[item.id] = item;
38061         this.items.push(item);
38062       //  if(this.resizeTabs){
38063     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38064   //         this.autoSizeTabs();
38065 //        }else{
38066 //            item.autoSize();
38067        // }
38068     },
38069
38070     /**
38071      * Removes a {@link Roo.TabPanelItem}.
38072      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38073      */
38074     removeTab : function(id){
38075         var items = this.items;
38076         var tab = items[id];
38077         if(!tab) { return; }
38078         var index = items.indexOf(tab);
38079         if(this.active == tab && items.length > 1){
38080             var newTab = this.getNextAvailable(index);
38081             if(newTab) {
38082                 newTab.activate();
38083             }
38084         }
38085         this.stripEl.dom.removeChild(tab.pnode.dom);
38086         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38087             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38088         }
38089         items.splice(index, 1);
38090         delete this.items[tab.id];
38091         tab.fireEvent("close", tab);
38092         tab.purgeListeners();
38093         this.autoSizeTabs();
38094     },
38095
38096     getNextAvailable : function(start){
38097         var items = this.items;
38098         var index = start;
38099         // look for a next tab that will slide over to
38100         // replace the one being removed
38101         while(index < items.length){
38102             var item = items[++index];
38103             if(item && !item.isHidden()){
38104                 return item;
38105             }
38106         }
38107         // if one isn't found select the previous tab (on the left)
38108         index = start;
38109         while(index >= 0){
38110             var item = items[--index];
38111             if(item && !item.isHidden()){
38112                 return item;
38113             }
38114         }
38115         return null;
38116     },
38117
38118     /**
38119      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38120      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38121      */
38122     disableTab : function(id){
38123         var tab = this.items[id];
38124         if(tab && this.active != tab){
38125             tab.disable();
38126         }
38127     },
38128
38129     /**
38130      * Enables a {@link Roo.TabPanelItem} that is disabled.
38131      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38132      */
38133     enableTab : function(id){
38134         var tab = this.items[id];
38135         tab.enable();
38136     },
38137
38138     /**
38139      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38140      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38141      * @return {Roo.TabPanelItem} The TabPanelItem.
38142      */
38143     activate : function(id){
38144         var tab = this.items[id];
38145         if(!tab){
38146             return null;
38147         }
38148         if(tab == this.active || tab.disabled){
38149             return tab;
38150         }
38151         var e = {};
38152         this.fireEvent("beforetabchange", this, e, tab);
38153         if(e.cancel !== true && !tab.disabled){
38154             if(this.active){
38155                 this.active.hide();
38156             }
38157             this.active = this.items[id];
38158             this.active.show();
38159             this.fireEvent("tabchange", this, this.active);
38160         }
38161         return tab;
38162     },
38163
38164     /**
38165      * Gets the active {@link Roo.TabPanelItem}.
38166      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38167      */
38168     getActiveTab : function(){
38169         return this.active;
38170     },
38171
38172     /**
38173      * Updates the tab body element to fit the height of the container element
38174      * for overflow scrolling
38175      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38176      */
38177     syncHeight : function(targetHeight){
38178         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38179         var bm = this.bodyEl.getMargins();
38180         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38181         this.bodyEl.setHeight(newHeight);
38182         return newHeight;
38183     },
38184
38185     onResize : function(){
38186         if(this.monitorResize){
38187             this.autoSizeTabs();
38188         }
38189     },
38190
38191     /**
38192      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38193      */
38194     beginUpdate : function(){
38195         this.updating = true;
38196     },
38197
38198     /**
38199      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38200      */
38201     endUpdate : function(){
38202         this.updating = false;
38203         this.autoSizeTabs();
38204     },
38205
38206     /**
38207      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38208      */
38209     autoSizeTabs : function(){
38210         var count = this.items.length;
38211         var vcount = count - this.hiddenCount;
38212         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38213             return;
38214         }
38215         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38216         var availWidth = Math.floor(w / vcount);
38217         var b = this.stripBody;
38218         if(b.getWidth() > w){
38219             var tabs = this.items;
38220             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38221             if(availWidth < this.minTabWidth){
38222                 /*if(!this.sleft){    // incomplete scrolling code
38223                     this.createScrollButtons();
38224                 }
38225                 this.showScroll();
38226                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38227             }
38228         }else{
38229             if(this.currentTabWidth < this.preferredTabWidth){
38230                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38231             }
38232         }
38233     },
38234
38235     /**
38236      * Returns the number of tabs in this TabPanel.
38237      * @return {Number}
38238      */
38239      getCount : function(){
38240          return this.items.length;
38241      },
38242
38243     /**
38244      * Resizes all the tabs to the passed width
38245      * @param {Number} The new width
38246      */
38247     setTabWidth : function(width){
38248         this.currentTabWidth = width;
38249         for(var i = 0, len = this.items.length; i < len; i++) {
38250                 if(!this.items[i].isHidden()) {
38251                 this.items[i].setWidth(width);
38252             }
38253         }
38254     },
38255
38256     /**
38257      * Destroys this TabPanel
38258      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38259      */
38260     destroy : function(removeEl){
38261         Roo.EventManager.removeResizeListener(this.onResize, this);
38262         for(var i = 0, len = this.items.length; i < len; i++){
38263             this.items[i].purgeListeners();
38264         }
38265         if(removeEl === true){
38266             this.el.update("");
38267             this.el.remove();
38268         }
38269     },
38270     
38271     createStrip : function(container)
38272     {
38273         var strip = document.createElement("nav");
38274         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38275         container.appendChild(strip);
38276         return strip;
38277     },
38278     
38279     createStripList : function(strip)
38280     {
38281         // div wrapper for retard IE
38282         // returns the "tr" element.
38283         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38284         //'<div class="x-tabs-strip-wrap">'+
38285           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38286           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38287         return strip.firstChild; //.firstChild.firstChild.firstChild;
38288     },
38289     createBody : function(container)
38290     {
38291         var body = document.createElement("div");
38292         Roo.id(body, "tab-body");
38293         //Roo.fly(body).addClass("x-tabs-body");
38294         Roo.fly(body).addClass("tab-content");
38295         container.appendChild(body);
38296         return body;
38297     },
38298     createItemBody :function(bodyEl, id){
38299         var body = Roo.getDom(id);
38300         if(!body){
38301             body = document.createElement("div");
38302             body.id = id;
38303         }
38304         //Roo.fly(body).addClass("x-tabs-item-body");
38305         Roo.fly(body).addClass("tab-pane");
38306          bodyEl.insertBefore(body, bodyEl.firstChild);
38307         return body;
38308     },
38309     /** @private */
38310     createStripElements :  function(stripEl, text, closable, tpl)
38311     {
38312         var td = document.createElement("li"); // was td..
38313         
38314         
38315         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38316         
38317         
38318         stripEl.appendChild(td);
38319         /*if(closable){
38320             td.className = "x-tabs-closable";
38321             if(!this.closeTpl){
38322                 this.closeTpl = new Roo.Template(
38323                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38324                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38325                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38326                 );
38327             }
38328             var el = this.closeTpl.overwrite(td, {"text": text});
38329             var close = el.getElementsByTagName("div")[0];
38330             var inner = el.getElementsByTagName("em")[0];
38331             return {"el": el, "close": close, "inner": inner};
38332         } else {
38333         */
38334         // not sure what this is..
38335 //            if(!this.tabTpl){
38336                 //this.tabTpl = new Roo.Template(
38337                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38338                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38339                 //);
38340 //                this.tabTpl = new Roo.Template(
38341 //                   '<a href="#">' +
38342 //                   '<span unselectable="on"' +
38343 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38344 //                            ' >{text}</span></a>'
38345 //                );
38346 //                
38347 //            }
38348
38349
38350             var template = tpl || this.tabTpl || false;
38351             
38352             if(!template){
38353                 
38354                 template = new Roo.Template(
38355                    '<a href="#">' +
38356                    '<span unselectable="on"' +
38357                             (this.disableTooltips ? '' : ' title="{text}"') +
38358                             ' >{text}</span></a>'
38359                 );
38360             }
38361             
38362             switch (typeof(template)) {
38363                 case 'object' :
38364                     break;
38365                 case 'string' :
38366                     template = new Roo.Template(template);
38367                     break;
38368                 default :
38369                     break;
38370             }
38371             
38372             var el = template.overwrite(td, {"text": text});
38373             
38374             var inner = el.getElementsByTagName("span")[0];
38375             
38376             return {"el": el, "inner": inner};
38377             
38378     }
38379         
38380     
38381 });
38382
38383 /**
38384  * @class Roo.TabPanelItem
38385  * @extends Roo.util.Observable
38386  * Represents an individual item (tab plus body) in a TabPanel.
38387  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38388  * @param {String} id The id of this TabPanelItem
38389  * @param {String} text The text for the tab of this TabPanelItem
38390  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38391  */
38392 Roo.bootstrap.panel.TabItem = function(config){
38393     /**
38394      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38395      * @type Roo.TabPanel
38396      */
38397     this.tabPanel = config.panel;
38398     /**
38399      * The id for this TabPanelItem
38400      * @type String
38401      */
38402     this.id = config.id;
38403     /** @private */
38404     this.disabled = false;
38405     /** @private */
38406     this.text = config.text;
38407     /** @private */
38408     this.loaded = false;
38409     this.closable = config.closable;
38410
38411     /**
38412      * The body element for this TabPanelItem.
38413      * @type Roo.Element
38414      */
38415     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38416     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38417     this.bodyEl.setStyle("display", "block");
38418     this.bodyEl.setStyle("zoom", "1");
38419     //this.hideAction();
38420
38421     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38422     /** @private */
38423     this.el = Roo.get(els.el);
38424     this.inner = Roo.get(els.inner, true);
38425     this.textEl = Roo.get(this.el.dom.firstChild, true);
38426     this.pnode = Roo.get(els.el.parentNode, true);
38427 //    this.el.on("mousedown", this.onTabMouseDown, this);
38428     this.el.on("click", this.onTabClick, this);
38429     /** @private */
38430     if(config.closable){
38431         var c = Roo.get(els.close, true);
38432         c.dom.title = this.closeText;
38433         c.addClassOnOver("close-over");
38434         c.on("click", this.closeClick, this);
38435      }
38436
38437     this.addEvents({
38438          /**
38439          * @event activate
38440          * Fires when this tab becomes the active tab.
38441          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38442          * @param {Roo.TabPanelItem} this
38443          */
38444         "activate": true,
38445         /**
38446          * @event beforeclose
38447          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38448          * @param {Roo.TabPanelItem} this
38449          * @param {Object} e Set cancel to true on this object to cancel the close.
38450          */
38451         "beforeclose": true,
38452         /**
38453          * @event close
38454          * Fires when this tab is closed.
38455          * @param {Roo.TabPanelItem} this
38456          */
38457          "close": true,
38458         /**
38459          * @event deactivate
38460          * Fires when this tab is no longer the active tab.
38461          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38462          * @param {Roo.TabPanelItem} this
38463          */
38464          "deactivate" : true
38465     });
38466     this.hidden = false;
38467
38468     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38469 };
38470
38471 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38472            {
38473     purgeListeners : function(){
38474        Roo.util.Observable.prototype.purgeListeners.call(this);
38475        this.el.removeAllListeners();
38476     },
38477     /**
38478      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38479      */
38480     show : function(){
38481         this.pnode.addClass("active");
38482         this.showAction();
38483         if(Roo.isOpera){
38484             this.tabPanel.stripWrap.repaint();
38485         }
38486         this.fireEvent("activate", this.tabPanel, this);
38487     },
38488
38489     /**
38490      * Returns true if this tab is the active tab.
38491      * @return {Boolean}
38492      */
38493     isActive : function(){
38494         return this.tabPanel.getActiveTab() == this;
38495     },
38496
38497     /**
38498      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38499      */
38500     hide : function(){
38501         this.pnode.removeClass("active");
38502         this.hideAction();
38503         this.fireEvent("deactivate", this.tabPanel, this);
38504     },
38505
38506     hideAction : function(){
38507         this.bodyEl.hide();
38508         this.bodyEl.setStyle("position", "absolute");
38509         this.bodyEl.setLeft("-20000px");
38510         this.bodyEl.setTop("-20000px");
38511     },
38512
38513     showAction : function(){
38514         this.bodyEl.setStyle("position", "relative");
38515         this.bodyEl.setTop("");
38516         this.bodyEl.setLeft("");
38517         this.bodyEl.show();
38518     },
38519
38520     /**
38521      * Set the tooltip for the tab.
38522      * @param {String} tooltip The tab's tooltip
38523      */
38524     setTooltip : function(text){
38525         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38526             this.textEl.dom.qtip = text;
38527             this.textEl.dom.removeAttribute('title');
38528         }else{
38529             this.textEl.dom.title = text;
38530         }
38531     },
38532
38533     onTabClick : function(e){
38534         e.preventDefault();
38535         this.tabPanel.activate(this.id);
38536     },
38537
38538     onTabMouseDown : function(e){
38539         e.preventDefault();
38540         this.tabPanel.activate(this.id);
38541     },
38542 /*
38543     getWidth : function(){
38544         return this.inner.getWidth();
38545     },
38546
38547     setWidth : function(width){
38548         var iwidth = width - this.pnode.getPadding("lr");
38549         this.inner.setWidth(iwidth);
38550         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38551         this.pnode.setWidth(width);
38552     },
38553 */
38554     /**
38555      * Show or hide the tab
38556      * @param {Boolean} hidden True to hide or false to show.
38557      */
38558     setHidden : function(hidden){
38559         this.hidden = hidden;
38560         this.pnode.setStyle("display", hidden ? "none" : "");
38561     },
38562
38563     /**
38564      * Returns true if this tab is "hidden"
38565      * @return {Boolean}
38566      */
38567     isHidden : function(){
38568         return this.hidden;
38569     },
38570
38571     /**
38572      * Returns the text for this tab
38573      * @return {String}
38574      */
38575     getText : function(){
38576         return this.text;
38577     },
38578     /*
38579     autoSize : function(){
38580         //this.el.beginMeasure();
38581         this.textEl.setWidth(1);
38582         /*
38583          *  #2804 [new] Tabs in Roojs
38584          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38585          */
38586         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38587         //this.el.endMeasure();
38588     //},
38589
38590     /**
38591      * Sets the text for the tab (Note: this also sets the tooltip text)
38592      * @param {String} text The tab's text and tooltip
38593      */
38594     setText : function(text){
38595         this.text = text;
38596         this.textEl.update(text);
38597         this.setTooltip(text);
38598         //if(!this.tabPanel.resizeTabs){
38599         //    this.autoSize();
38600         //}
38601     },
38602     /**
38603      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38604      */
38605     activate : function(){
38606         this.tabPanel.activate(this.id);
38607     },
38608
38609     /**
38610      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38611      */
38612     disable : function(){
38613         if(this.tabPanel.active != this){
38614             this.disabled = true;
38615             this.pnode.addClass("disabled");
38616         }
38617     },
38618
38619     /**
38620      * Enables this TabPanelItem if it was previously disabled.
38621      */
38622     enable : function(){
38623         this.disabled = false;
38624         this.pnode.removeClass("disabled");
38625     },
38626
38627     /**
38628      * Sets the content for this TabPanelItem.
38629      * @param {String} content The content
38630      * @param {Boolean} loadScripts true to look for and load scripts
38631      */
38632     setContent : function(content, loadScripts){
38633         this.bodyEl.update(content, loadScripts);
38634     },
38635
38636     /**
38637      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38638      * @return {Roo.UpdateManager} The UpdateManager
38639      */
38640     getUpdateManager : function(){
38641         return this.bodyEl.getUpdateManager();
38642     },
38643
38644     /**
38645      * Set a URL to be used to load the content for this TabPanelItem.
38646      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38647      * @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)
38648      * @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)
38649      * @return {Roo.UpdateManager} The UpdateManager
38650      */
38651     setUrl : function(url, params, loadOnce){
38652         if(this.refreshDelegate){
38653             this.un('activate', this.refreshDelegate);
38654         }
38655         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38656         this.on("activate", this.refreshDelegate);
38657         return this.bodyEl.getUpdateManager();
38658     },
38659
38660     /** @private */
38661     _handleRefresh : function(url, params, loadOnce){
38662         if(!loadOnce || !this.loaded){
38663             var updater = this.bodyEl.getUpdateManager();
38664             updater.update(url, params, this._setLoaded.createDelegate(this));
38665         }
38666     },
38667
38668     /**
38669      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38670      *   Will fail silently if the setUrl method has not been called.
38671      *   This does not activate the panel, just updates its content.
38672      */
38673     refresh : function(){
38674         if(this.refreshDelegate){
38675            this.loaded = false;
38676            this.refreshDelegate();
38677         }
38678     },
38679
38680     /** @private */
38681     _setLoaded : function(){
38682         this.loaded = true;
38683     },
38684
38685     /** @private */
38686     closeClick : function(e){
38687         var o = {};
38688         e.stopEvent();
38689         this.fireEvent("beforeclose", this, o);
38690         if(o.cancel !== true){
38691             this.tabPanel.removeTab(this.id);
38692         }
38693     },
38694     /**
38695      * The text displayed in the tooltip for the close icon.
38696      * @type String
38697      */
38698     closeText : "Close this tab"
38699 });
38700 /**
38701 *    This script refer to:
38702 *    Title: International Telephone Input
38703 *    Author: Jack O'Connor
38704 *    Code version:  v12.1.12
38705 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38706 **/
38707
38708 Roo.bootstrap.PhoneInputData = function() {
38709     var d = [
38710       [
38711         "Afghanistan (‫افغانستان‬‎)",
38712         "af",
38713         "93"
38714       ],
38715       [
38716         "Albania (Shqipëri)",
38717         "al",
38718         "355"
38719       ],
38720       [
38721         "Algeria (‫الجزائر‬‎)",
38722         "dz",
38723         "213"
38724       ],
38725       [
38726         "American Samoa",
38727         "as",
38728         "1684"
38729       ],
38730       [
38731         "Andorra",
38732         "ad",
38733         "376"
38734       ],
38735       [
38736         "Angola",
38737         "ao",
38738         "244"
38739       ],
38740       [
38741         "Anguilla",
38742         "ai",
38743         "1264"
38744       ],
38745       [
38746         "Antigua and Barbuda",
38747         "ag",
38748         "1268"
38749       ],
38750       [
38751         "Argentina",
38752         "ar",
38753         "54"
38754       ],
38755       [
38756         "Armenia (Հայաստան)",
38757         "am",
38758         "374"
38759       ],
38760       [
38761         "Aruba",
38762         "aw",
38763         "297"
38764       ],
38765       [
38766         "Australia",
38767         "au",
38768         "61",
38769         0
38770       ],
38771       [
38772         "Austria (Österreich)",
38773         "at",
38774         "43"
38775       ],
38776       [
38777         "Azerbaijan (Azərbaycan)",
38778         "az",
38779         "994"
38780       ],
38781       [
38782         "Bahamas",
38783         "bs",
38784         "1242"
38785       ],
38786       [
38787         "Bahrain (‫البحرين‬‎)",
38788         "bh",
38789         "973"
38790       ],
38791       [
38792         "Bangladesh (বাংলাদেশ)",
38793         "bd",
38794         "880"
38795       ],
38796       [
38797         "Barbados",
38798         "bb",
38799         "1246"
38800       ],
38801       [
38802         "Belarus (Беларусь)",
38803         "by",
38804         "375"
38805       ],
38806       [
38807         "Belgium (België)",
38808         "be",
38809         "32"
38810       ],
38811       [
38812         "Belize",
38813         "bz",
38814         "501"
38815       ],
38816       [
38817         "Benin (Bénin)",
38818         "bj",
38819         "229"
38820       ],
38821       [
38822         "Bermuda",
38823         "bm",
38824         "1441"
38825       ],
38826       [
38827         "Bhutan (འབྲུག)",
38828         "bt",
38829         "975"
38830       ],
38831       [
38832         "Bolivia",
38833         "bo",
38834         "591"
38835       ],
38836       [
38837         "Bosnia and Herzegovina (Босна и Херцеговина)",
38838         "ba",
38839         "387"
38840       ],
38841       [
38842         "Botswana",
38843         "bw",
38844         "267"
38845       ],
38846       [
38847         "Brazil (Brasil)",
38848         "br",
38849         "55"
38850       ],
38851       [
38852         "British Indian Ocean Territory",
38853         "io",
38854         "246"
38855       ],
38856       [
38857         "British Virgin Islands",
38858         "vg",
38859         "1284"
38860       ],
38861       [
38862         "Brunei",
38863         "bn",
38864         "673"
38865       ],
38866       [
38867         "Bulgaria (България)",
38868         "bg",
38869         "359"
38870       ],
38871       [
38872         "Burkina Faso",
38873         "bf",
38874         "226"
38875       ],
38876       [
38877         "Burundi (Uburundi)",
38878         "bi",
38879         "257"
38880       ],
38881       [
38882         "Cambodia (កម្ពុជា)",
38883         "kh",
38884         "855"
38885       ],
38886       [
38887         "Cameroon (Cameroun)",
38888         "cm",
38889         "237"
38890       ],
38891       [
38892         "Canada",
38893         "ca",
38894         "1",
38895         1,
38896         ["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"]
38897       ],
38898       [
38899         "Cape Verde (Kabu Verdi)",
38900         "cv",
38901         "238"
38902       ],
38903       [
38904         "Caribbean Netherlands",
38905         "bq",
38906         "599",
38907         1
38908       ],
38909       [
38910         "Cayman Islands",
38911         "ky",
38912         "1345"
38913       ],
38914       [
38915         "Central African Republic (République centrafricaine)",
38916         "cf",
38917         "236"
38918       ],
38919       [
38920         "Chad (Tchad)",
38921         "td",
38922         "235"
38923       ],
38924       [
38925         "Chile",
38926         "cl",
38927         "56"
38928       ],
38929       [
38930         "China (中国)",
38931         "cn",
38932         "86"
38933       ],
38934       [
38935         "Christmas Island",
38936         "cx",
38937         "61",
38938         2
38939       ],
38940       [
38941         "Cocos (Keeling) Islands",
38942         "cc",
38943         "61",
38944         1
38945       ],
38946       [
38947         "Colombia",
38948         "co",
38949         "57"
38950       ],
38951       [
38952         "Comoros (‫جزر القمر‬‎)",
38953         "km",
38954         "269"
38955       ],
38956       [
38957         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38958         "cd",
38959         "243"
38960       ],
38961       [
38962         "Congo (Republic) (Congo-Brazzaville)",
38963         "cg",
38964         "242"
38965       ],
38966       [
38967         "Cook Islands",
38968         "ck",
38969         "682"
38970       ],
38971       [
38972         "Costa Rica",
38973         "cr",
38974         "506"
38975       ],
38976       [
38977         "Côte d’Ivoire",
38978         "ci",
38979         "225"
38980       ],
38981       [
38982         "Croatia (Hrvatska)",
38983         "hr",
38984         "385"
38985       ],
38986       [
38987         "Cuba",
38988         "cu",
38989         "53"
38990       ],
38991       [
38992         "Curaçao",
38993         "cw",
38994         "599",
38995         0
38996       ],
38997       [
38998         "Cyprus (Κύπρος)",
38999         "cy",
39000         "357"
39001       ],
39002       [
39003         "Czech Republic (Česká republika)",
39004         "cz",
39005         "420"
39006       ],
39007       [
39008         "Denmark (Danmark)",
39009         "dk",
39010         "45"
39011       ],
39012       [
39013         "Djibouti",
39014         "dj",
39015         "253"
39016       ],
39017       [
39018         "Dominica",
39019         "dm",
39020         "1767"
39021       ],
39022       [
39023         "Dominican Republic (República Dominicana)",
39024         "do",
39025         "1",
39026         2,
39027         ["809", "829", "849"]
39028       ],
39029       [
39030         "Ecuador",
39031         "ec",
39032         "593"
39033       ],
39034       [
39035         "Egypt (‫مصر‬‎)",
39036         "eg",
39037         "20"
39038       ],
39039       [
39040         "El Salvador",
39041         "sv",
39042         "503"
39043       ],
39044       [
39045         "Equatorial Guinea (Guinea Ecuatorial)",
39046         "gq",
39047         "240"
39048       ],
39049       [
39050         "Eritrea",
39051         "er",
39052         "291"
39053       ],
39054       [
39055         "Estonia (Eesti)",
39056         "ee",
39057         "372"
39058       ],
39059       [
39060         "Ethiopia",
39061         "et",
39062         "251"
39063       ],
39064       [
39065         "Falkland Islands (Islas Malvinas)",
39066         "fk",
39067         "500"
39068       ],
39069       [
39070         "Faroe Islands (Føroyar)",
39071         "fo",
39072         "298"
39073       ],
39074       [
39075         "Fiji",
39076         "fj",
39077         "679"
39078       ],
39079       [
39080         "Finland (Suomi)",
39081         "fi",
39082         "358",
39083         0
39084       ],
39085       [
39086         "France",
39087         "fr",
39088         "33"
39089       ],
39090       [
39091         "French Guiana (Guyane française)",
39092         "gf",
39093         "594"
39094       ],
39095       [
39096         "French Polynesia (Polynésie française)",
39097         "pf",
39098         "689"
39099       ],
39100       [
39101         "Gabon",
39102         "ga",
39103         "241"
39104       ],
39105       [
39106         "Gambia",
39107         "gm",
39108         "220"
39109       ],
39110       [
39111         "Georgia (საქართველო)",
39112         "ge",
39113         "995"
39114       ],
39115       [
39116         "Germany (Deutschland)",
39117         "de",
39118         "49"
39119       ],
39120       [
39121         "Ghana (Gaana)",
39122         "gh",
39123         "233"
39124       ],
39125       [
39126         "Gibraltar",
39127         "gi",
39128         "350"
39129       ],
39130       [
39131         "Greece (Ελλάδα)",
39132         "gr",
39133         "30"
39134       ],
39135       [
39136         "Greenland (Kalaallit Nunaat)",
39137         "gl",
39138         "299"
39139       ],
39140       [
39141         "Grenada",
39142         "gd",
39143         "1473"
39144       ],
39145       [
39146         "Guadeloupe",
39147         "gp",
39148         "590",
39149         0
39150       ],
39151       [
39152         "Guam",
39153         "gu",
39154         "1671"
39155       ],
39156       [
39157         "Guatemala",
39158         "gt",
39159         "502"
39160       ],
39161       [
39162         "Guernsey",
39163         "gg",
39164         "44",
39165         1
39166       ],
39167       [
39168         "Guinea (Guinée)",
39169         "gn",
39170         "224"
39171       ],
39172       [
39173         "Guinea-Bissau (Guiné Bissau)",
39174         "gw",
39175         "245"
39176       ],
39177       [
39178         "Guyana",
39179         "gy",
39180         "592"
39181       ],
39182       [
39183         "Haiti",
39184         "ht",
39185         "509"
39186       ],
39187       [
39188         "Honduras",
39189         "hn",
39190         "504"
39191       ],
39192       [
39193         "Hong Kong (香港)",
39194         "hk",
39195         "852"
39196       ],
39197       [
39198         "Hungary (Magyarország)",
39199         "hu",
39200         "36"
39201       ],
39202       [
39203         "Iceland (Ísland)",
39204         "is",
39205         "354"
39206       ],
39207       [
39208         "India (भारत)",
39209         "in",
39210         "91"
39211       ],
39212       [
39213         "Indonesia",
39214         "id",
39215         "62"
39216       ],
39217       [
39218         "Iran (‫ایران‬‎)",
39219         "ir",
39220         "98"
39221       ],
39222       [
39223         "Iraq (‫العراق‬‎)",
39224         "iq",
39225         "964"
39226       ],
39227       [
39228         "Ireland",
39229         "ie",
39230         "353"
39231       ],
39232       [
39233         "Isle of Man",
39234         "im",
39235         "44",
39236         2
39237       ],
39238       [
39239         "Israel (‫ישראל‬‎)",
39240         "il",
39241         "972"
39242       ],
39243       [
39244         "Italy (Italia)",
39245         "it",
39246         "39",
39247         0
39248       ],
39249       [
39250         "Jamaica",
39251         "jm",
39252         "1876"
39253       ],
39254       [
39255         "Japan (日本)",
39256         "jp",
39257         "81"
39258       ],
39259       [
39260         "Jersey",
39261         "je",
39262         "44",
39263         3
39264       ],
39265       [
39266         "Jordan (‫الأردن‬‎)",
39267         "jo",
39268         "962"
39269       ],
39270       [
39271         "Kazakhstan (Казахстан)",
39272         "kz",
39273         "7",
39274         1
39275       ],
39276       [
39277         "Kenya",
39278         "ke",
39279         "254"
39280       ],
39281       [
39282         "Kiribati",
39283         "ki",
39284         "686"
39285       ],
39286       [
39287         "Kosovo",
39288         "xk",
39289         "383"
39290       ],
39291       [
39292         "Kuwait (‫الكويت‬‎)",
39293         "kw",
39294         "965"
39295       ],
39296       [
39297         "Kyrgyzstan (Кыргызстан)",
39298         "kg",
39299         "996"
39300       ],
39301       [
39302         "Laos (ລາວ)",
39303         "la",
39304         "856"
39305       ],
39306       [
39307         "Latvia (Latvija)",
39308         "lv",
39309         "371"
39310       ],
39311       [
39312         "Lebanon (‫لبنان‬‎)",
39313         "lb",
39314         "961"
39315       ],
39316       [
39317         "Lesotho",
39318         "ls",
39319         "266"
39320       ],
39321       [
39322         "Liberia",
39323         "lr",
39324         "231"
39325       ],
39326       [
39327         "Libya (‫ليبيا‬‎)",
39328         "ly",
39329         "218"
39330       ],
39331       [
39332         "Liechtenstein",
39333         "li",
39334         "423"
39335       ],
39336       [
39337         "Lithuania (Lietuva)",
39338         "lt",
39339         "370"
39340       ],
39341       [
39342         "Luxembourg",
39343         "lu",
39344         "352"
39345       ],
39346       [
39347         "Macau (澳門)",
39348         "mo",
39349         "853"
39350       ],
39351       [
39352         "Macedonia (FYROM) (Македонија)",
39353         "mk",
39354         "389"
39355       ],
39356       [
39357         "Madagascar (Madagasikara)",
39358         "mg",
39359         "261"
39360       ],
39361       [
39362         "Malawi",
39363         "mw",
39364         "265"
39365       ],
39366       [
39367         "Malaysia",
39368         "my",
39369         "60"
39370       ],
39371       [
39372         "Maldives",
39373         "mv",
39374         "960"
39375       ],
39376       [
39377         "Mali",
39378         "ml",
39379         "223"
39380       ],
39381       [
39382         "Malta",
39383         "mt",
39384         "356"
39385       ],
39386       [
39387         "Marshall Islands",
39388         "mh",
39389         "692"
39390       ],
39391       [
39392         "Martinique",
39393         "mq",
39394         "596"
39395       ],
39396       [
39397         "Mauritania (‫موريتانيا‬‎)",
39398         "mr",
39399         "222"
39400       ],
39401       [
39402         "Mauritius (Moris)",
39403         "mu",
39404         "230"
39405       ],
39406       [
39407         "Mayotte",
39408         "yt",
39409         "262",
39410         1
39411       ],
39412       [
39413         "Mexico (México)",
39414         "mx",
39415         "52"
39416       ],
39417       [
39418         "Micronesia",
39419         "fm",
39420         "691"
39421       ],
39422       [
39423         "Moldova (Republica Moldova)",
39424         "md",
39425         "373"
39426       ],
39427       [
39428         "Monaco",
39429         "mc",
39430         "377"
39431       ],
39432       [
39433         "Mongolia (Монгол)",
39434         "mn",
39435         "976"
39436       ],
39437       [
39438         "Montenegro (Crna Gora)",
39439         "me",
39440         "382"
39441       ],
39442       [
39443         "Montserrat",
39444         "ms",
39445         "1664"
39446       ],
39447       [
39448         "Morocco (‫المغرب‬‎)",
39449         "ma",
39450         "212",
39451         0
39452       ],
39453       [
39454         "Mozambique (Moçambique)",
39455         "mz",
39456         "258"
39457       ],
39458       [
39459         "Myanmar (Burma) (မြန်မာ)",
39460         "mm",
39461         "95"
39462       ],
39463       [
39464         "Namibia (Namibië)",
39465         "na",
39466         "264"
39467       ],
39468       [
39469         "Nauru",
39470         "nr",
39471         "674"
39472       ],
39473       [
39474         "Nepal (नेपाल)",
39475         "np",
39476         "977"
39477       ],
39478       [
39479         "Netherlands (Nederland)",
39480         "nl",
39481         "31"
39482       ],
39483       [
39484         "New Caledonia (Nouvelle-Calédonie)",
39485         "nc",
39486         "687"
39487       ],
39488       [
39489         "New Zealand",
39490         "nz",
39491         "64"
39492       ],
39493       [
39494         "Nicaragua",
39495         "ni",
39496         "505"
39497       ],
39498       [
39499         "Niger (Nijar)",
39500         "ne",
39501         "227"
39502       ],
39503       [
39504         "Nigeria",
39505         "ng",
39506         "234"
39507       ],
39508       [
39509         "Niue",
39510         "nu",
39511         "683"
39512       ],
39513       [
39514         "Norfolk Island",
39515         "nf",
39516         "672"
39517       ],
39518       [
39519         "North Korea (조선 민주주의 인민 공화국)",
39520         "kp",
39521         "850"
39522       ],
39523       [
39524         "Northern Mariana Islands",
39525         "mp",
39526         "1670"
39527       ],
39528       [
39529         "Norway (Norge)",
39530         "no",
39531         "47",
39532         0
39533       ],
39534       [
39535         "Oman (‫عُمان‬‎)",
39536         "om",
39537         "968"
39538       ],
39539       [
39540         "Pakistan (‫پاکستان‬‎)",
39541         "pk",
39542         "92"
39543       ],
39544       [
39545         "Palau",
39546         "pw",
39547         "680"
39548       ],
39549       [
39550         "Palestine (‫فلسطين‬‎)",
39551         "ps",
39552         "970"
39553       ],
39554       [
39555         "Panama (Panamá)",
39556         "pa",
39557         "507"
39558       ],
39559       [
39560         "Papua New Guinea",
39561         "pg",
39562         "675"
39563       ],
39564       [
39565         "Paraguay",
39566         "py",
39567         "595"
39568       ],
39569       [
39570         "Peru (Perú)",
39571         "pe",
39572         "51"
39573       ],
39574       [
39575         "Philippines",
39576         "ph",
39577         "63"
39578       ],
39579       [
39580         "Poland (Polska)",
39581         "pl",
39582         "48"
39583       ],
39584       [
39585         "Portugal",
39586         "pt",
39587         "351"
39588       ],
39589       [
39590         "Puerto Rico",
39591         "pr",
39592         "1",
39593         3,
39594         ["787", "939"]
39595       ],
39596       [
39597         "Qatar (‫قطر‬‎)",
39598         "qa",
39599         "974"
39600       ],
39601       [
39602         "Réunion (La Réunion)",
39603         "re",
39604         "262",
39605         0
39606       ],
39607       [
39608         "Romania (România)",
39609         "ro",
39610         "40"
39611       ],
39612       [
39613         "Russia (Россия)",
39614         "ru",
39615         "7",
39616         0
39617       ],
39618       [
39619         "Rwanda",
39620         "rw",
39621         "250"
39622       ],
39623       [
39624         "Saint Barthélemy",
39625         "bl",
39626         "590",
39627         1
39628       ],
39629       [
39630         "Saint Helena",
39631         "sh",
39632         "290"
39633       ],
39634       [
39635         "Saint Kitts and Nevis",
39636         "kn",
39637         "1869"
39638       ],
39639       [
39640         "Saint Lucia",
39641         "lc",
39642         "1758"
39643       ],
39644       [
39645         "Saint Martin (Saint-Martin (partie française))",
39646         "mf",
39647         "590",
39648         2
39649       ],
39650       [
39651         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39652         "pm",
39653         "508"
39654       ],
39655       [
39656         "Saint Vincent and the Grenadines",
39657         "vc",
39658         "1784"
39659       ],
39660       [
39661         "Samoa",
39662         "ws",
39663         "685"
39664       ],
39665       [
39666         "San Marino",
39667         "sm",
39668         "378"
39669       ],
39670       [
39671         "São Tomé and Príncipe (São Tomé e Príncipe)",
39672         "st",
39673         "239"
39674       ],
39675       [
39676         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39677         "sa",
39678         "966"
39679       ],
39680       [
39681         "Senegal (Sénégal)",
39682         "sn",
39683         "221"
39684       ],
39685       [
39686         "Serbia (Србија)",
39687         "rs",
39688         "381"
39689       ],
39690       [
39691         "Seychelles",
39692         "sc",
39693         "248"
39694       ],
39695       [
39696         "Sierra Leone",
39697         "sl",
39698         "232"
39699       ],
39700       [
39701         "Singapore",
39702         "sg",
39703         "65"
39704       ],
39705       [
39706         "Sint Maarten",
39707         "sx",
39708         "1721"
39709       ],
39710       [
39711         "Slovakia (Slovensko)",
39712         "sk",
39713         "421"
39714       ],
39715       [
39716         "Slovenia (Slovenija)",
39717         "si",
39718         "386"
39719       ],
39720       [
39721         "Solomon Islands",
39722         "sb",
39723         "677"
39724       ],
39725       [
39726         "Somalia (Soomaaliya)",
39727         "so",
39728         "252"
39729       ],
39730       [
39731         "South Africa",
39732         "za",
39733         "27"
39734       ],
39735       [
39736         "South Korea (대한민국)",
39737         "kr",
39738         "82"
39739       ],
39740       [
39741         "South Sudan (‫جنوب السودان‬‎)",
39742         "ss",
39743         "211"
39744       ],
39745       [
39746         "Spain (España)",
39747         "es",
39748         "34"
39749       ],
39750       [
39751         "Sri Lanka (ශ්‍රී ලංකාව)",
39752         "lk",
39753         "94"
39754       ],
39755       [
39756         "Sudan (‫السودان‬‎)",
39757         "sd",
39758         "249"
39759       ],
39760       [
39761         "Suriname",
39762         "sr",
39763         "597"
39764       ],
39765       [
39766         "Svalbard and Jan Mayen",
39767         "sj",
39768         "47",
39769         1
39770       ],
39771       [
39772         "Swaziland",
39773         "sz",
39774         "268"
39775       ],
39776       [
39777         "Sweden (Sverige)",
39778         "se",
39779         "46"
39780       ],
39781       [
39782         "Switzerland (Schweiz)",
39783         "ch",
39784         "41"
39785       ],
39786       [
39787         "Syria (‫سوريا‬‎)",
39788         "sy",
39789         "963"
39790       ],
39791       [
39792         "Taiwan (台灣)",
39793         "tw",
39794         "886"
39795       ],
39796       [
39797         "Tajikistan",
39798         "tj",
39799         "992"
39800       ],
39801       [
39802         "Tanzania",
39803         "tz",
39804         "255"
39805       ],
39806       [
39807         "Thailand (ไทย)",
39808         "th",
39809         "66"
39810       ],
39811       [
39812         "Timor-Leste",
39813         "tl",
39814         "670"
39815       ],
39816       [
39817         "Togo",
39818         "tg",
39819         "228"
39820       ],
39821       [
39822         "Tokelau",
39823         "tk",
39824         "690"
39825       ],
39826       [
39827         "Tonga",
39828         "to",
39829         "676"
39830       ],
39831       [
39832         "Trinidad and Tobago",
39833         "tt",
39834         "1868"
39835       ],
39836       [
39837         "Tunisia (‫تونس‬‎)",
39838         "tn",
39839         "216"
39840       ],
39841       [
39842         "Turkey (Türkiye)",
39843         "tr",
39844         "90"
39845       ],
39846       [
39847         "Turkmenistan",
39848         "tm",
39849         "993"
39850       ],
39851       [
39852         "Turks and Caicos Islands",
39853         "tc",
39854         "1649"
39855       ],
39856       [
39857         "Tuvalu",
39858         "tv",
39859         "688"
39860       ],
39861       [
39862         "U.S. Virgin Islands",
39863         "vi",
39864         "1340"
39865       ],
39866       [
39867         "Uganda",
39868         "ug",
39869         "256"
39870       ],
39871       [
39872         "Ukraine (Україна)",
39873         "ua",
39874         "380"
39875       ],
39876       [
39877         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39878         "ae",
39879         "971"
39880       ],
39881       [
39882         "United Kingdom",
39883         "gb",
39884         "44",
39885         0
39886       ],
39887       [
39888         "United States",
39889         "us",
39890         "1",
39891         0
39892       ],
39893       [
39894         "Uruguay",
39895         "uy",
39896         "598"
39897       ],
39898       [
39899         "Uzbekistan (Oʻzbekiston)",
39900         "uz",
39901         "998"
39902       ],
39903       [
39904         "Vanuatu",
39905         "vu",
39906         "678"
39907       ],
39908       [
39909         "Vatican City (Città del Vaticano)",
39910         "va",
39911         "39",
39912         1
39913       ],
39914       [
39915         "Venezuela",
39916         "ve",
39917         "58"
39918       ],
39919       [
39920         "Vietnam (Việt Nam)",
39921         "vn",
39922         "84"
39923       ],
39924       [
39925         "Wallis and Futuna (Wallis-et-Futuna)",
39926         "wf",
39927         "681"
39928       ],
39929       [
39930         "Western Sahara (‫الصحراء الغربية‬‎)",
39931         "eh",
39932         "212",
39933         1
39934       ],
39935       [
39936         "Yemen (‫اليمن‬‎)",
39937         "ye",
39938         "967"
39939       ],
39940       [
39941         "Zambia",
39942         "zm",
39943         "260"
39944       ],
39945       [
39946         "Zimbabwe",
39947         "zw",
39948         "263"
39949       ],
39950       [
39951         "Åland Islands",
39952         "ax",
39953         "358",
39954         1
39955       ]
39956   ];
39957   
39958   return d;
39959 }/**
39960 *    This script refer to:
39961 *    Title: International Telephone Input
39962 *    Author: Jack O'Connor
39963 *    Code version:  v12.1.12
39964 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39965 **/
39966
39967 /**
39968  * @class Roo.bootstrap.PhoneInput
39969  * @extends Roo.bootstrap.TriggerField
39970  * An input with International dial-code selection
39971  
39972  * @cfg {String} defaultDialCode default '+852'
39973  * @cfg {Array} preferedCountries default []
39974   
39975  * @constructor
39976  * Create a new PhoneInput.
39977  * @param {Object} config Configuration options
39978  */
39979
39980 Roo.bootstrap.PhoneInput = function(config) {
39981     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39982 };
39983
39984 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39985         
39986         listWidth: undefined,
39987         
39988         selectedClass: 'active',
39989         
39990         invalidClass : "has-warning",
39991         
39992         validClass: 'has-success',
39993         
39994         allowed: '0123456789',
39995         
39996         max_length: 15,
39997         
39998         /**
39999          * @cfg {String} defaultDialCode The default dial code when initializing the input
40000          */
40001         defaultDialCode: '+852',
40002         
40003         /**
40004          * @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
40005          */
40006         preferedCountries: false,
40007         
40008         getAutoCreate : function()
40009         {
40010             var data = Roo.bootstrap.PhoneInputData();
40011             var align = this.labelAlign || this.parentLabelAlign();
40012             var id = Roo.id();
40013             
40014             this.allCountries = [];
40015             this.dialCodeMapping = [];
40016             
40017             for (var i = 0; i < data.length; i++) {
40018               var c = data[i];
40019               this.allCountries[i] = {
40020                 name: c[0],
40021                 iso2: c[1],
40022                 dialCode: c[2],
40023                 priority: c[3] || 0,
40024                 areaCodes: c[4] || null
40025               };
40026               this.dialCodeMapping[c[2]] = {
40027                   name: c[0],
40028                   iso2: c[1],
40029                   priority: c[3] || 0,
40030                   areaCodes: c[4] || null
40031               };
40032             }
40033             
40034             var cfg = {
40035                 cls: 'form-group',
40036                 cn: []
40037             };
40038             
40039             var input =  {
40040                 tag: 'input',
40041                 id : id,
40042                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40043                 maxlength: this.max_length,
40044                 cls : 'form-control tel-input',
40045                 autocomplete: 'new-password'
40046             };
40047             
40048             var hiddenInput = {
40049                 tag: 'input',
40050                 type: 'hidden',
40051                 cls: 'hidden-tel-input'
40052             };
40053             
40054             if (this.name) {
40055                 hiddenInput.name = this.name;
40056             }
40057             
40058             if (this.disabled) {
40059                 input.disabled = true;
40060             }
40061             
40062             var flag_container = {
40063                 tag: 'div',
40064                 cls: 'flag-box',
40065                 cn: [
40066                     {
40067                         tag: 'div',
40068                         cls: 'flag'
40069                     },
40070                     {
40071                         tag: 'div',
40072                         cls: 'caret'
40073                     }
40074                 ]
40075             };
40076             
40077             var box = {
40078                 tag: 'div',
40079                 cls: this.hasFeedback ? 'has-feedback' : '',
40080                 cn: [
40081                     hiddenInput,
40082                     input,
40083                     {
40084                         tag: 'input',
40085                         cls: 'dial-code-holder',
40086                         disabled: true
40087                     }
40088                 ]
40089             };
40090             
40091             var container = {
40092                 cls: 'roo-select2-container input-group',
40093                 cn: [
40094                     flag_container,
40095                     box
40096                 ]
40097             };
40098             
40099             if (this.fieldLabel.length) {
40100                 var indicator = {
40101                     tag: 'i',
40102                     tooltip: 'This field is required'
40103                 };
40104                 
40105                 var label = {
40106                     tag: 'label',
40107                     'for':  id,
40108                     cls: 'control-label',
40109                     cn: []
40110                 };
40111                 
40112                 var label_text = {
40113                     tag: 'span',
40114                     html: this.fieldLabel
40115                 };
40116                 
40117                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40118                 label.cn = [
40119                     indicator,
40120                     label_text
40121                 ];
40122                 
40123                 if(this.indicatorpos == 'right') {
40124                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40125                     label.cn = [
40126                         label_text,
40127                         indicator
40128                     ];
40129                 }
40130                 
40131                 if(align == 'left') {
40132                     container = {
40133                         tag: 'div',
40134                         cn: [
40135                             container
40136                         ]
40137                     };
40138                     
40139                     if(this.labelWidth > 12){
40140                         label.style = "width: " + this.labelWidth + 'px';
40141                     }
40142                     if(this.labelWidth < 13 && this.labelmd == 0){
40143                         this.labelmd = this.labelWidth;
40144                     }
40145                     if(this.labellg > 0){
40146                         label.cls += ' col-lg-' + this.labellg;
40147                         input.cls += ' col-lg-' + (12 - this.labellg);
40148                     }
40149                     if(this.labelmd > 0){
40150                         label.cls += ' col-md-' + this.labelmd;
40151                         container.cls += ' col-md-' + (12 - this.labelmd);
40152                     }
40153                     if(this.labelsm > 0){
40154                         label.cls += ' col-sm-' + this.labelsm;
40155                         container.cls += ' col-sm-' + (12 - this.labelsm);
40156                     }
40157                     if(this.labelxs > 0){
40158                         label.cls += ' col-xs-' + this.labelxs;
40159                         container.cls += ' col-xs-' + (12 - this.labelxs);
40160                     }
40161                 }
40162             }
40163             
40164             cfg.cn = [
40165                 label,
40166                 container
40167             ];
40168             
40169             var settings = this;
40170             
40171             ['xs','sm','md','lg'].map(function(size){
40172                 if (settings[size]) {
40173                     cfg.cls += ' col-' + size + '-' + settings[size];
40174                 }
40175             });
40176             
40177             this.store = new Roo.data.Store({
40178                 proxy : new Roo.data.MemoryProxy({}),
40179                 reader : new Roo.data.JsonReader({
40180                     fields : [
40181                         {
40182                             'name' : 'name',
40183                             'type' : 'string'
40184                         },
40185                         {
40186                             'name' : 'iso2',
40187                             'type' : 'string'
40188                         },
40189                         {
40190                             'name' : 'dialCode',
40191                             'type' : 'string'
40192                         },
40193                         {
40194                             'name' : 'priority',
40195                             'type' : 'string'
40196                         },
40197                         {
40198                             'name' : 'areaCodes',
40199                             'type' : 'string'
40200                         }
40201                     ]
40202                 })
40203             });
40204             
40205             if(!this.preferedCountries) {
40206                 this.preferedCountries = [
40207                     'hk',
40208                     'gb',
40209                     'us'
40210                 ];
40211             }
40212             
40213             var p = this.preferedCountries.reverse();
40214             
40215             if(p) {
40216                 for (var i = 0; i < p.length; i++) {
40217                     for (var j = 0; j < this.allCountries.length; j++) {
40218                         if(this.allCountries[j].iso2 == p[i]) {
40219                             var t = this.allCountries[j];
40220                             this.allCountries.splice(j,1);
40221                             this.allCountries.unshift(t);
40222                         }
40223                     } 
40224                 }
40225             }
40226             
40227             this.store.proxy.data = {
40228                 success: true,
40229                 data: this.allCountries
40230             };
40231             
40232             return cfg;
40233         },
40234         
40235         initEvents : function()
40236         {
40237             this.createList();
40238             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40239             
40240             this.indicator = this.indicatorEl();
40241             this.flag = this.flagEl();
40242             this.dialCodeHolder = this.dialCodeHolderEl();
40243             
40244             this.trigger = this.el.select('div.flag-box',true).first();
40245             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40246             
40247             var _this = this;
40248             
40249             (function(){
40250                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40251                 _this.list.setWidth(lw);
40252             }).defer(100);
40253             
40254             this.list.on('mouseover', this.onViewOver, this);
40255             this.list.on('mousemove', this.onViewMove, this);
40256             this.inputEl().on("keyup", this.onKeyUp, this);
40257             this.inputEl().on("keypress", this.onKeyPress, this);
40258             
40259             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40260
40261             this.view = new Roo.View(this.list, this.tpl, {
40262                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40263             });
40264             
40265             this.view.on('click', this.onViewClick, this);
40266             this.setValue(this.defaultDialCode);
40267         },
40268         
40269         onTriggerClick : function(e)
40270         {
40271             Roo.log('trigger click');
40272             if(this.disabled){
40273                 return;
40274             }
40275             
40276             if(this.isExpanded()){
40277                 this.collapse();
40278                 this.hasFocus = false;
40279             }else {
40280                 this.store.load({});
40281                 this.hasFocus = true;
40282                 this.expand();
40283             }
40284         },
40285         
40286         isExpanded : function()
40287         {
40288             return this.list.isVisible();
40289         },
40290         
40291         collapse : function()
40292         {
40293             if(!this.isExpanded()){
40294                 return;
40295             }
40296             this.list.hide();
40297             Roo.get(document).un('mousedown', this.collapseIf, this);
40298             Roo.get(document).un('mousewheel', this.collapseIf, this);
40299             this.fireEvent('collapse', this);
40300             this.validate();
40301         },
40302         
40303         expand : function()
40304         {
40305             Roo.log('expand');
40306
40307             if(this.isExpanded() || !this.hasFocus){
40308                 return;
40309             }
40310             
40311             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40312             this.list.setWidth(lw);
40313             
40314             this.list.show();
40315             this.restrictHeight();
40316             
40317             Roo.get(document).on('mousedown', this.collapseIf, this);
40318             Roo.get(document).on('mousewheel', this.collapseIf, this);
40319             
40320             this.fireEvent('expand', this);
40321         },
40322         
40323         restrictHeight : function()
40324         {
40325             this.list.alignTo(this.inputEl(), this.listAlign);
40326             this.list.alignTo(this.inputEl(), this.listAlign);
40327         },
40328         
40329         onViewOver : function(e, t)
40330         {
40331             if(this.inKeyMode){
40332                 return;
40333             }
40334             var item = this.view.findItemFromChild(t);
40335             
40336             if(item){
40337                 var index = this.view.indexOf(item);
40338                 this.select(index, false);
40339             }
40340         },
40341
40342         // private
40343         onViewClick : function(view, doFocus, el, e)
40344         {
40345             var index = this.view.getSelectedIndexes()[0];
40346             
40347             var r = this.store.getAt(index);
40348             
40349             if(r){
40350                 this.onSelect(r, index);
40351             }
40352             if(doFocus !== false && !this.blockFocus){
40353                 this.inputEl().focus();
40354             }
40355         },
40356         
40357         onViewMove : function(e, t)
40358         {
40359             this.inKeyMode = false;
40360         },
40361         
40362         select : function(index, scrollIntoView)
40363         {
40364             this.selectedIndex = index;
40365             this.view.select(index);
40366             if(scrollIntoView !== false){
40367                 var el = this.view.getNode(index);
40368                 if(el){
40369                     this.list.scrollChildIntoView(el, false);
40370                 }
40371             }
40372         },
40373         
40374         createList : function()
40375         {
40376             this.list = Roo.get(document.body).createChild({
40377                 tag: 'ul',
40378                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40379                 style: 'display:none'
40380             });
40381             
40382             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40383         },
40384         
40385         collapseIf : function(e)
40386         {
40387             var in_combo  = e.within(this.el);
40388             var in_list =  e.within(this.list);
40389             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40390             
40391             if (in_combo || in_list || is_list) {
40392                 return;
40393             }
40394             this.collapse();
40395         },
40396         
40397         onSelect : function(record, index)
40398         {
40399             if(this.fireEvent('beforeselect', this, record, index) !== false){
40400                 
40401                 this.setFlagClass(record.data.iso2);
40402                 this.setDialCode(record.data.dialCode);
40403                 this.hasFocus = false;
40404                 this.collapse();
40405                 this.fireEvent('select', this, record, index);
40406             }
40407         },
40408         
40409         flagEl : function()
40410         {
40411             var flag = this.el.select('div.flag',true).first();
40412             if(!flag){
40413                 return false;
40414             }
40415             return flag;
40416         },
40417         
40418         dialCodeHolderEl : function()
40419         {
40420             var d = this.el.select('input.dial-code-holder',true).first();
40421             if(!d){
40422                 return false;
40423             }
40424             return d;
40425         },
40426         
40427         setDialCode : function(v)
40428         {
40429             this.dialCodeHolder.dom.value = '+'+v;
40430         },
40431         
40432         setFlagClass : function(n)
40433         {
40434             this.flag.dom.className = 'flag '+n;
40435         },
40436         
40437         getValue : function()
40438         {
40439             var v = this.inputEl().getValue();
40440             if(this.dialCodeHolder) {
40441                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40442             }
40443             return v;
40444         },
40445         
40446         setValue : function(v)
40447         {
40448             var d = this.getDialCode(v);
40449             
40450             //invalid dial code
40451             if(v.length == 0 || !d || d.length == 0) {
40452                 if(this.rendered){
40453                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40454                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40455                 }
40456                 return;
40457             }
40458             
40459             //valid dial code
40460             this.setFlagClass(this.dialCodeMapping[d].iso2);
40461             this.setDialCode(d);
40462             this.inputEl().dom.value = v.replace('+'+d,'');
40463             this.hiddenEl().dom.value = this.getValue();
40464             
40465             this.validate();
40466         },
40467         
40468         getDialCode : function(v)
40469         {
40470             v = v ||  '';
40471             
40472             if (v.length == 0) {
40473                 return this.dialCodeHolder.dom.value;
40474             }
40475             
40476             var dialCode = "";
40477             if (v.charAt(0) != "+") {
40478                 return false;
40479             }
40480             var numericChars = "";
40481             for (var i = 1; i < v.length; i++) {
40482               var c = v.charAt(i);
40483               if (!isNaN(c)) {
40484                 numericChars += c;
40485                 if (this.dialCodeMapping[numericChars]) {
40486                   dialCode = v.substr(1, i);
40487                 }
40488                 if (numericChars.length == 4) {
40489                   break;
40490                 }
40491               }
40492             }
40493             return dialCode;
40494         },
40495         
40496         reset : function()
40497         {
40498             this.setValue(this.defaultDialCode);
40499             this.validate();
40500         },
40501         
40502         hiddenEl : function()
40503         {
40504             return this.el.select('input.hidden-tel-input',true).first();
40505         },
40506         
40507         // after setting val
40508         onKeyUp : function(e){
40509             this.setValue(this.getValue());
40510         },
40511         
40512         onKeyPress : function(e){
40513             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40514                 e.stopEvent();
40515             }
40516         }
40517         
40518 });
40519 /**
40520  * @class Roo.bootstrap.MoneyField
40521  * @extends Roo.bootstrap.ComboBox
40522  * Bootstrap MoneyField class
40523  * 
40524  * @constructor
40525  * Create a new MoneyField.
40526  * @param {Object} config Configuration options
40527  */
40528
40529 Roo.bootstrap.MoneyField = function(config) {
40530     
40531     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40532     
40533 };
40534
40535 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40536     
40537     /**
40538      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40539      */
40540     allowDecimals : true,
40541     /**
40542      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40543      */
40544     decimalSeparator : ".",
40545     /**
40546      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40547      */
40548     decimalPrecision : 0,
40549     /**
40550      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40551      */
40552     allowNegative : true,
40553     /**
40554      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40555      */
40556     allowZero: true,
40557     /**
40558      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40559      */
40560     minValue : Number.NEGATIVE_INFINITY,
40561     /**
40562      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40563      */
40564     maxValue : Number.MAX_VALUE,
40565     /**
40566      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40567      */
40568     minText : "The minimum value for this field is {0}",
40569     /**
40570      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40571      */
40572     maxText : "The maximum value for this field is {0}",
40573     /**
40574      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40575      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40576      */
40577     nanText : "{0} is not a valid number",
40578     /**
40579      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40580      */
40581     castInt : true,
40582     /**
40583      * @cfg {String} defaults currency of the MoneyField
40584      * value should be in lkey
40585      */
40586     defaultCurrency : false,
40587     /**
40588      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40589      */
40590     thousandsDelimiter : false,
40591     /**
40592      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40593      */
40594     max_length: false,
40595     
40596     inputlg : 9,
40597     inputmd : 9,
40598     inputsm : 9,
40599     inputxs : 6,
40600     
40601     store : false,
40602     
40603     getAutoCreate : function()
40604     {
40605         var align = this.labelAlign || this.parentLabelAlign();
40606         
40607         var id = Roo.id();
40608
40609         var cfg = {
40610             cls: 'form-group',
40611             cn: []
40612         };
40613
40614         var input =  {
40615             tag: 'input',
40616             id : id,
40617             cls : 'form-control roo-money-amount-input',
40618             autocomplete: 'new-password'
40619         };
40620         
40621         var hiddenInput = {
40622             tag: 'input',
40623             type: 'hidden',
40624             id: Roo.id(),
40625             cls: 'hidden-number-input'
40626         };
40627         
40628         if(this.max_length) {
40629             input.maxlength = this.max_length; 
40630         }
40631         
40632         if (this.name) {
40633             hiddenInput.name = this.name;
40634         }
40635
40636         if (this.disabled) {
40637             input.disabled = true;
40638         }
40639
40640         var clg = 12 - this.inputlg;
40641         var cmd = 12 - this.inputmd;
40642         var csm = 12 - this.inputsm;
40643         var cxs = 12 - this.inputxs;
40644         
40645         var container = {
40646             tag : 'div',
40647             cls : 'row roo-money-field',
40648             cn : [
40649                 {
40650                     tag : 'div',
40651                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40652                     cn : [
40653                         {
40654                             tag : 'div',
40655                             cls: 'roo-select2-container input-group',
40656                             cn: [
40657                                 {
40658                                     tag : 'input',
40659                                     cls : 'form-control roo-money-currency-input',
40660                                     autocomplete: 'new-password',
40661                                     readOnly : 1,
40662                                     name : this.currencyName
40663                                 },
40664                                 {
40665                                     tag :'span',
40666                                     cls : 'input-group-addon',
40667                                     cn : [
40668                                         {
40669                                             tag: 'span',
40670                                             cls: 'caret'
40671                                         }
40672                                     ]
40673                                 }
40674                             ]
40675                         }
40676                     ]
40677                 },
40678                 {
40679                     tag : 'div',
40680                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40681                     cn : [
40682                         {
40683                             tag: 'div',
40684                             cls: this.hasFeedback ? 'has-feedback' : '',
40685                             cn: [
40686                                 input
40687                             ]
40688                         }
40689                     ]
40690                 }
40691             ]
40692             
40693         };
40694         
40695         if (this.fieldLabel.length) {
40696             var indicator = {
40697                 tag: 'i',
40698                 tooltip: 'This field is required'
40699             };
40700
40701             var label = {
40702                 tag: 'label',
40703                 'for':  id,
40704                 cls: 'control-label',
40705                 cn: []
40706             };
40707
40708             var label_text = {
40709                 tag: 'span',
40710                 html: this.fieldLabel
40711             };
40712
40713             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40714             label.cn = [
40715                 indicator,
40716                 label_text
40717             ];
40718
40719             if(this.indicatorpos == 'right') {
40720                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40721                 label.cn = [
40722                     label_text,
40723                     indicator
40724                 ];
40725             }
40726
40727             if(align == 'left') {
40728                 container = {
40729                     tag: 'div',
40730                     cn: [
40731                         container
40732                     ]
40733                 };
40734
40735                 if(this.labelWidth > 12){
40736                     label.style = "width: " + this.labelWidth + 'px';
40737                 }
40738                 if(this.labelWidth < 13 && this.labelmd == 0){
40739                     this.labelmd = this.labelWidth;
40740                 }
40741                 if(this.labellg > 0){
40742                     label.cls += ' col-lg-' + this.labellg;
40743                     input.cls += ' col-lg-' + (12 - this.labellg);
40744                 }
40745                 if(this.labelmd > 0){
40746                     label.cls += ' col-md-' + this.labelmd;
40747                     container.cls += ' col-md-' + (12 - this.labelmd);
40748                 }
40749                 if(this.labelsm > 0){
40750                     label.cls += ' col-sm-' + this.labelsm;
40751                     container.cls += ' col-sm-' + (12 - this.labelsm);
40752                 }
40753                 if(this.labelxs > 0){
40754                     label.cls += ' col-xs-' + this.labelxs;
40755                     container.cls += ' col-xs-' + (12 - this.labelxs);
40756                 }
40757             }
40758         }
40759
40760         cfg.cn = [
40761             label,
40762             container,
40763             hiddenInput
40764         ];
40765         
40766         var settings = this;
40767
40768         ['xs','sm','md','lg'].map(function(size){
40769             if (settings[size]) {
40770                 cfg.cls += ' col-' + size + '-' + settings[size];
40771             }
40772         });
40773         
40774         return cfg;
40775     },
40776     
40777     initEvents : function()
40778     {
40779         this.indicator = this.indicatorEl();
40780         
40781         this.initCurrencyEvent();
40782         
40783         this.initNumberEvent();
40784     },
40785     
40786     initCurrencyEvent : function()
40787     {
40788         if (!this.store) {
40789             throw "can not find store for combo";
40790         }
40791         
40792         this.store = Roo.factory(this.store, Roo.data);
40793         this.store.parent = this;
40794         
40795         this.createList();
40796         
40797         this.triggerEl = this.el.select('.input-group-addon', true).first();
40798         
40799         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40800         
40801         var _this = this;
40802         
40803         (function(){
40804             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40805             _this.list.setWidth(lw);
40806         }).defer(100);
40807         
40808         this.list.on('mouseover', this.onViewOver, this);
40809         this.list.on('mousemove', this.onViewMove, this);
40810         this.list.on('scroll', this.onViewScroll, this);
40811         
40812         if(!this.tpl){
40813             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40814         }
40815         
40816         this.view = new Roo.View(this.list, this.tpl, {
40817             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40818         });
40819         
40820         this.view.on('click', this.onViewClick, this);
40821         
40822         this.store.on('beforeload', this.onBeforeLoad, this);
40823         this.store.on('load', this.onLoad, this);
40824         this.store.on('loadexception', this.onLoadException, this);
40825         
40826         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40827             "up" : function(e){
40828                 this.inKeyMode = true;
40829                 this.selectPrev();
40830             },
40831
40832             "down" : function(e){
40833                 if(!this.isExpanded()){
40834                     this.onTriggerClick();
40835                 }else{
40836                     this.inKeyMode = true;
40837                     this.selectNext();
40838                 }
40839             },
40840
40841             "enter" : function(e){
40842                 this.collapse();
40843                 
40844                 if(this.fireEvent("specialkey", this, e)){
40845                     this.onViewClick(false);
40846                 }
40847                 
40848                 return true;
40849             },
40850
40851             "esc" : function(e){
40852                 this.collapse();
40853             },
40854
40855             "tab" : function(e){
40856                 this.collapse();
40857                 
40858                 if(this.fireEvent("specialkey", this, e)){
40859                     this.onViewClick(false);
40860                 }
40861                 
40862                 return true;
40863             },
40864
40865             scope : this,
40866
40867             doRelay : function(foo, bar, hname){
40868                 if(hname == 'down' || this.scope.isExpanded()){
40869                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40870                 }
40871                 return true;
40872             },
40873
40874             forceKeyDown: true
40875         });
40876         
40877         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40878         
40879     },
40880     
40881     initNumberEvent : function(e)
40882     {
40883         this.inputEl().on("keydown" , this.fireKey,  this);
40884         this.inputEl().on("focus", this.onFocus,  this);
40885         this.inputEl().on("blur", this.onBlur,  this);
40886         
40887         this.inputEl().relayEvent('keyup', this);
40888         
40889         if(this.indicator){
40890             this.indicator.addClass('invisible');
40891         }
40892  
40893         this.originalValue = this.getValue();
40894         
40895         if(this.validationEvent == 'keyup'){
40896             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40897             this.inputEl().on('keyup', this.filterValidation, this);
40898         }
40899         else if(this.validationEvent !== false){
40900             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40901         }
40902         
40903         if(this.selectOnFocus){
40904             this.on("focus", this.preFocus, this);
40905             
40906         }
40907         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40908             this.inputEl().on("keypress", this.filterKeys, this);
40909         } else {
40910             this.inputEl().relayEvent('keypress', this);
40911         }
40912         
40913         var allowed = "0123456789";
40914         
40915         if(this.allowDecimals){
40916             allowed += this.decimalSeparator;
40917         }
40918         
40919         if(this.allowNegative){
40920             allowed += "-";
40921         }
40922         
40923         if(this.thousandsDelimiter) {
40924             allowed += ",";
40925         }
40926         
40927         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40928         
40929         var keyPress = function(e){
40930             
40931             var k = e.getKey();
40932             
40933             var c = e.getCharCode();
40934             
40935             if(
40936                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40937                     allowed.indexOf(String.fromCharCode(c)) === -1
40938             ){
40939                 e.stopEvent();
40940                 return;
40941             }
40942             
40943             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40944                 return;
40945             }
40946             
40947             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40948                 e.stopEvent();
40949             }
40950         };
40951         
40952         this.inputEl().on("keypress", keyPress, this);
40953         
40954     },
40955     
40956     onTriggerClick : function(e)
40957     {   
40958         if(this.disabled){
40959             return;
40960         }
40961         
40962         this.page = 0;
40963         this.loadNext = false;
40964         
40965         if(this.isExpanded()){
40966             this.collapse();
40967             return;
40968         }
40969         
40970         this.hasFocus = true;
40971         
40972         if(this.triggerAction == 'all') {
40973             this.doQuery(this.allQuery, true);
40974             return;
40975         }
40976         
40977         this.doQuery(this.getRawValue());
40978     },
40979     
40980     getCurrency : function()
40981     {   
40982         var v = this.currencyEl().getValue();
40983         
40984         return v;
40985     },
40986     
40987     restrictHeight : function()
40988     {
40989         this.list.alignTo(this.currencyEl(), this.listAlign);
40990         this.list.alignTo(this.currencyEl(), this.listAlign);
40991     },
40992     
40993     onViewClick : function(view, doFocus, el, e)
40994     {
40995         var index = this.view.getSelectedIndexes()[0];
40996         
40997         var r = this.store.getAt(index);
40998         
40999         if(r){
41000             this.onSelect(r, index);
41001         }
41002     },
41003     
41004     onSelect : function(record, index){
41005         
41006         if(this.fireEvent('beforeselect', this, record, index) !== false){
41007         
41008             this.setFromCurrencyData(index > -1 ? record.data : false);
41009             
41010             this.collapse();
41011             
41012             this.fireEvent('select', this, record, index);
41013         }
41014     },
41015     
41016     setFromCurrencyData : function(o)
41017     {
41018         var currency = '';
41019         
41020         this.lastCurrency = o;
41021         
41022         if (this.currencyField) {
41023             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41024         } else {
41025             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41026         }
41027         
41028         this.lastSelectionText = currency;
41029         
41030         //setting default currency
41031         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41032             this.setCurrency(this.defaultCurrency);
41033             return;
41034         }
41035         
41036         this.setCurrency(currency);
41037     },
41038     
41039     setFromData : function(o)
41040     {
41041         var c = {};
41042         
41043         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41044         
41045         this.setFromCurrencyData(c);
41046         
41047         var value = '';
41048         
41049         if (this.name) {
41050             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41051         } else {
41052             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41053         }
41054         
41055         this.setValue(value);
41056         
41057     },
41058     
41059     setCurrency : function(v)
41060     {   
41061         this.currencyValue = v;
41062         
41063         if(this.rendered){
41064             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41065             this.validate();
41066         }
41067     },
41068     
41069     setValue : function(v)
41070     {
41071         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41072         
41073         this.value = v;
41074         
41075         if(this.rendered){
41076             
41077             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41078             
41079             this.inputEl().dom.value = (v == '') ? '' :
41080                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41081             
41082             if(!this.allowZero && v === '0') {
41083                 this.hiddenEl().dom.value = '';
41084                 this.inputEl().dom.value = '';
41085             }
41086             
41087             this.validate();
41088         }
41089     },
41090     
41091     getRawValue : function()
41092     {
41093         var v = this.inputEl().getValue();
41094         
41095         return v;
41096     },
41097     
41098     getValue : function()
41099     {
41100         return this.fixPrecision(this.parseValue(this.getRawValue()));
41101     },
41102     
41103     parseValue : function(value)
41104     {
41105         if(this.thousandsDelimiter) {
41106             value += "";
41107             r = new RegExp(",", "g");
41108             value = value.replace(r, "");
41109         }
41110         
41111         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41112         return isNaN(value) ? '' : value;
41113         
41114     },
41115     
41116     fixPrecision : function(value)
41117     {
41118         if(this.thousandsDelimiter) {
41119             value += "";
41120             r = new RegExp(",", "g");
41121             value = value.replace(r, "");
41122         }
41123         
41124         var nan = isNaN(value);
41125         
41126         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41127             return nan ? '' : value;
41128         }
41129         return parseFloat(value).toFixed(this.decimalPrecision);
41130     },
41131     
41132     decimalPrecisionFcn : function(v)
41133     {
41134         return Math.floor(v);
41135     },
41136     
41137     validateValue : function(value)
41138     {
41139         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41140             return false;
41141         }
41142         
41143         var num = this.parseValue(value);
41144         
41145         if(isNaN(num)){
41146             this.markInvalid(String.format(this.nanText, value));
41147             return false;
41148         }
41149         
41150         if(num < this.minValue){
41151             this.markInvalid(String.format(this.minText, this.minValue));
41152             return false;
41153         }
41154         
41155         if(num > this.maxValue){
41156             this.markInvalid(String.format(this.maxText, this.maxValue));
41157             return false;
41158         }
41159         
41160         return true;
41161     },
41162     
41163     validate : function()
41164     {
41165         if(this.disabled || this.allowBlank){
41166             this.markValid();
41167             return true;
41168         }
41169         
41170         var currency = this.getCurrency();
41171         
41172         if(this.validateValue(this.getRawValue()) && currency.length){
41173             this.markValid();
41174             return true;
41175         }
41176         
41177         this.markInvalid();
41178         return false;
41179     },
41180     
41181     getName: function()
41182     {
41183         return this.name;
41184     },
41185     
41186     beforeBlur : function()
41187     {
41188         if(!this.castInt){
41189             return;
41190         }
41191         
41192         var v = this.parseValue(this.getRawValue());
41193         
41194         if(v || v == 0){
41195             this.setValue(v);
41196         }
41197     },
41198     
41199     onBlur : function()
41200     {
41201         this.beforeBlur();
41202         
41203         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41204             //this.el.removeClass(this.focusClass);
41205         }
41206         
41207         this.hasFocus = false;
41208         
41209         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41210             this.validate();
41211         }
41212         
41213         var v = this.getValue();
41214         
41215         if(String(v) !== String(this.startValue)){
41216             this.fireEvent('change', this, v, this.startValue);
41217         }
41218         
41219         this.fireEvent("blur", this);
41220     },
41221     
41222     inputEl : function()
41223     {
41224         return this.el.select('.roo-money-amount-input', true).first();
41225     },
41226     
41227     currencyEl : function()
41228     {
41229         return this.el.select('.roo-money-currency-input', true).first();
41230     },
41231     
41232     hiddenEl : function()
41233     {
41234         return this.el.select('input.hidden-number-input',true).first();
41235     }
41236     
41237 });